编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Java修炼终极指南:39. 在字节和十六进制编码字符串之间进行转换

wxchong 2024-07-25 13:52:01 开源技术 10 ℃ 0 评论


在操纵文件/消息流、执行编码/解码任务、处理图像等应用中,将字节转换为十六进制(反之亦然)是一种常见的操作。Java字节是[-128, +127]范围内的数字,并使用1个有符号字节(8位)表示。十六进制(基数为16)是一个基于16个数字(0、1、2、3、4、5、6、7、8、9、A、B、C、D、E和F)的系统。换句话说,一个字节值的这8位正好可以容纳范围在00到FF之间的2个十六进制字符。十进制 <-> 二进制 <-> 十六进制映射关系如图所示:


图 2.27 - 十进制到二进制到十六进制转换

例如,122在二进制中是01111010。因为0111在十六进制中是7,而1010是A,所以结果是122在十六进制中是7A(也写作0x7A)。那么负字节呢?从上一章我们知道,Java使用正数的二进制补码来表示负数。这意味着-122在二进制中是10000110(保留正数122=1111010的前7位,翻转(1111010)=0000101,加1(0000001)=00000110,并附加符号位1,得到10000110),在十六进制中是0x86。将负数转换为十六进制有多种方法,但我们可以轻松地将较低的4位获取为10000110 & 0xF = 0110,较高的四位为(10000110 >> 4) & 0xF = 1000 & 0xF = 1000(这里,0xF(二进制,1111)掩码仅对负数有用)。由于0110=6,1000=8,所以10000110在十六进制中是0x86。

如果你需要深入了解Java中的位操作,或者你在理解当前主题时遇到问题,那么请考虑阅读《Java完整编码面试指南》的第9章。因此,在代码行中,我们可以依赖这个简单算法和`Character.forDigit(int d, int r)`,它返回给定基数(r)下给定数字(d)的字符表示:

public static String byteToHexString(byte v) {  
  int higher = (v >> 4) & 0xF;  
  int lower = v & 0xF;  
  String result = String.valueOf(  
    new char[]{  
      Character.forDigit(higher, 16),  
      Character.forDigit(lower, 16)}  
    );  
  return result;  
}


解决这个问题的方法有很多(在捆绑的代码中,你可以看到这种解决方案的另一种风格)。例如,我们知道`Integer.toHexString(int n)`方法返回一个字符串,该字符串表示给定参数的无符号整数在基数16下的值,那么我们只需要对负数应用0xFF(二进制,11111111)掩码即可:

public static String byteToHexString(byte v) {  
  return Integer.toHexString(v & 0xFF);  
}


如果有一种方法我们应该避免,那就是基于`String.format()`的方法。`String.format("%02x ", byte_nr)`这种方法简洁但非常慢!那么反向过程呢?将一个给定的十六进制字符串(例如,7d、09等)转换为字节相当简单。只需获取给定字符串的第一个(d1)和第二个(d2)字符,并应用关系`(byte) ((d1 << 4) + d2)`:

public static byte hexToByte(String s) {  
  int d1 = Character.digit(s.charAt(0), 16);  
  int d2 = Character.digit(s.charAt(1), 16);  
  return (byte) ((d1 << 4) + d2);  
}


捆绑的代码中提供了更多示例。如果你依赖第三方库,那么请查看Apache Commons Codec(Hex.encodeHexString())、Guava(BaseEncoding)、Spring Security(Hex.encode())、Bouncy Castle(Hex.toHexString())等,这个列表是开放的。

JDK 17+

从JDK 17开始,我们可以使用`java.util.HexFormat`类。这个类提供了许多静态方法来处理十六进制数,包括`String toHexDigits(byte value)`和`byte[] parseHex(CharSequence string)`。因此,我们可以将字节转换为十六进制字符串,如下所示:

public static String byteToHexString(byte v) {  
  HexFormat hex = HexFormat.of();  
  return hex.toHexDigits(v);  
}


反之亦然,如下所示:

public static byte hexToByte(String s) {  
  HexFormat hex = HexFormat.of();  
  return hex.parseHex(s)[0];  
}


在捆绑的代码中,你还可以看到这些解决方案的扩展,用于将字节数组(byte[])转换为字符串,反之亦然。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表