Java串口通信:从十六进制字符串到字节数组的正确转换与发送

简介: Java串口通信:从十六进制字符串到字节数组的正确转换与发送

在嵌入式开发、物联网、自动化控制等领域,串口通信作为一种常见且可靠的通信方式,扮演着重要角色。本文将聚焦于Java环境中串口通信的核心环节——如何正确地将十六进制字符串转换为字节数组,并通过串口发送。我们将探讨常见的误区、正确处理流程、相关代码实现及应用场景,帮助开发者避免混淆,提升串口数据传输的准确性和效率。

一、内容

1. 十六进制字符串与字节数组的关系

在串口通信中,数据通常以字节形式传输。有时,为了便于人眼阅读和编写,这些字节值会被编码为十六进制字符串。例如,字节序列“53 A8 C0 00 00 20 41 00…”代表一组待发送的字节数据。

2. 错误的处理方式与问题

在实际编程过程中,开发者可能会遇到如下两种典型错误:

错误一:直接调用 getBytes() 方法

String hexString = "53 A8 C0 00 00 20 41 00...";
byte[] data = hexString.getBytes();

因为Java的 getBytes() 方法不会直接接受十六进制字符串作为输入并将其转换为字节数组,而是将十六进制字符串视作普通文本,将每个字符转换为其对应的ASCII值,而非字节值。输出结果与预期不符。

错误二:未正确处理带空格的十六进制字符串

String hexString = "53 A8 C0 00 00 20 41 00...";
byte[] data = hexString.replaceAll("\\s", "").getBytes(StandardCharsets.UTF_8);

虽然去除了空格,但使用 getBytes() 方法依然会将字符转换为ASCII值。此外,UTF-8编码可能导致非预期的字节序列。

3. 正确的十六进制字符串转字节数组方法

private static byte[] hexStringToByteArray(String s) {
    String[] hexParts = s.split(" ");
    byte[] data = new byte[hexParts.length];
    for (int i = 0; i < hexParts.length; i++) {
        data[i] = (byte) Integer.parseInt(hexParts[i], 16);
    }
    return data;
}

此方法首先使用 split(" ") 将十六进制字符串按照空格分割成多个部分,然后逐个将每个部分转换为字节并放入数组中。


注意:如果字符串中的空格不规则,或者有额外的非十六进制字符,还需要进一步的预处理步骤来确保输入的有效性和准确性。

4.Java中字节溢出问题

Java中的byte类型是有符号的,其值域范围为-128到127。当处理十六进制数值时,如果该数值对应的十进制值超过127,转换为byte类型时会触发字节溢出,导致结果以负数形式呈现。


举例说明:十六进制A8转为十进制

以十六进制数A8为例,其对应的十进制数为168。然而,当我们将A8视作一个有符号的8位字节(即byte类型)时,其解释会发生变化。

  1. 十六进制转二进制A8对应的二进制形式为10101000
  2. 识别符号位:在8位带符号整数中,最高位(最左边的位)为符号位。对于10101000,最高位为1,表明这是一个负数。
  3. 计算负数绝对值:对于负数,其补码表示方法为:取反加一得到原码的补码。A8的补码为01010111,转换为十进制为87
  4. 得出最终负数:根据补码计算规则,负数的值等于-(-87 + 1),即-88。因此,在8位有符号整数的表示下,十六进制数A8对应着十进制的-88

综上所述,A8在Java的byte类型中表现为-88,而非其原本的十进制值168。这是由于byte类型的有符号特性以及其有限的值域范围(-128到127)导致的。为清晰展示字节数据的真实数值,可以先将其转换为无符号整数类型再进行打印或处理。

5.Java不支持无符号类型

Java编程语言并未像C或C++那样提供直接的无符号整数类型,如unsigned char。在Java中,所有的基本整数类型(如byteshortintlong)均是有符号的,意味着它们既能表示正数,也能表示负数。


当需要处理无符号字节数据时,可以采用特定方法模拟其行为。例如,当从串口接收一个字节并希望将其作为无符号8位整数对待时,可以将byte转换为int类型,并确保正确解析其正数值。以下是一个具体的转换示例:


byte signedByte = (byte) 0xA8; // 假设为从串口接收到的有符号字节数据
int unsignedInt = signedByte & 0xFF; // 通过位运算将字节转换为无符号整数
System.out.println(unsignedInt); // 输出:168


这里,使用位与操作符(&)与常量0xFF(二进制全1,相当于十进制的255)进行位运算。由于byte只有8位,其余高位均为0,与0xFF进行位与运算相当于保留其原有的8位二进制值,同时将高位补足为0,从而得到一个无符号的int值。


尽管原始的十六进制数A8(对应十进制168)在Java的byte类型中可能表现为负数(如-88),通过上述转换,我们能够准确地以无符号整数形式展示其原始数值168。这种转换方法在处理串口通信、网络协议、文件读写等涉及字节数据操作的场景中尤为有用,有助于消除因有符号溢出导致的负数表示带来的混淆,确保数据的正确解析。

二、总结

  • 区别:错误的处理方式将十六进制字符串视为普通文本,导致输出的是字符的ASCII值而非原始字节。正确的方法则是将每个十六进制字符对转换为对应的字节值。
  • 应用场景:串口设备配置、传感器数据采集、通信协议实现等场景均涉及十六进制字符串到字节数组的转换。


目录
相关文章
|
21天前
|
SQL Java 索引
java小工具util系列2:字符串工具
java小工具util系列2:字符串工具
137 83
|
1月前
|
存储 安全 Java
Java零基础-字符串详解
【10月更文挑战第18天】Java零基础教学篇,手把手实践教学!
106 60
|
21天前
|
Java 数据库
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
52 26
|
25天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
44 8
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
48 6
|
2月前
|
Java 数据库
案例一:去掉数据库某列中的所有英文,利用java正则表达式去做,核心:去掉字符串中的英文
这篇文章介绍了如何使用Java正则表达式从数据库某列中去除所有英文字符。
57 15
|
2月前
|
Java
JAVA易错点详解(数据类型转换、字符串与运算符)
JAVA易错点详解(数据类型转换、字符串与运算符)
53 4
|
3月前
|
Java 数据库
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
55 3
|
3月前
|
SQL Java 索引
java小工具util系列2:字符串工具
java小工具util系列2:字符串工具
22 2
|
3月前
|
存储 移动开发 Java
java核心之字符串与编码
java核心之字符串与编码
26 2