Java代码将字节转换为十六进制
我有一个字节数组。 我希望将该数组的每个字节字符串转换为其相应的十六进制值。
Java中有没有将字节数组转换为十六进制数的函数?
byte[] bytes = {-1, 0, 1, 2, 3 };
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
// prints "FF 00 01 02 03 "
也可以看看
java.util.Formatter
语法 %[flags][width]conversion
'0'
- 结果将被填零 2
'X'
- 结果被格式化为十六进制整数,大写 看看问题的文字,这也可能是这样的:
String[] arr = {"-1", "0", "10", "20" };
for (int i = 0; i < arr.length; i++) {
arr[i] = String.format("%02x", Byte.parseByte(arr[i]));
}
System.out.println(java.util.Arrays.toString(arr));
// prints "[ff, 00, 0a, 14]"
这里有几个答案使用Integer.toHexString(int)
; 这是可行的,但有一些注意事项。 由于该参数是一个int
,所以会对byte
参数执行扩展原语转换,这涉及到符号扩展。
byte b = -1;
System.out.println(Integer.toHexString(b));
// prints "ffffffff"
在Java中签名的8位byte
被符号扩展为32位int
。 为了有效地撤销这个符号扩展,可以用0xFF
来屏蔽该byte
。
byte b = -1;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "ff"
使用toHexString
另一个问题是它不toHexString
零:
byte b = 10;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "a"
这两个因素结合起来应该使String.format
解决方案更受欢迎。
参考
byte
,从-128
到127
(含) 我发布是因为没有任何现有答案可以解释为什么他们的方法可行,我认为这对于这个问题非常重要。 在某些情况下,这会导致所提出的解决方案显得不必要的复杂和微妙。 为了说明,我将提供一个非常简单的方法,但我会提供更多的细节来帮助说明为什么它的工作原理。
首先,我们要做什么? 我们希望将一个字节值(或一个字节数组)转换为一个代表ASCII码的十六进制值的字符串。 所以,第一步是找出Java中的一个字节究竟是什么:
字节数据类型是一个8位有符号二进制补码整数 。 它的最小值为-128,最大值为127(包括)。 字节数据类型对于在大型数组中保存内存很有用,其中内存节省实际上很重要。 在限制有助于澄清代码的情况下,它们也可以用来代替int; 变量范围有限这一事实可以用作文档的一种形式。
这是什么意思? 一些事情:首先也是最重要的,这意味着我们正在使用8位 。 举例来说,我们可以将数字2写为0000 0010.但是,由于它是二进制补码,因此我们写出如下所示的负数2:1111 1110.还有一点意思是,转换为十六进制非常简单。 也就是说,您只需将每个4位段直接转换为十六进制。 请注意,为了理解此方案中的负数,您首先需要了解二进制补码。 如果你还没有理解二进制补码,你可以在这里阅读一个很好的解释:http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
将二进制补码转换为十六进制
一旦数字处于二进制补码中,将其转换为十六进制就很简单。 通常,从二进制转换为十六进制非常简单,正如您在接下来的两个示例中所看到的,您可以直接从二进制补码转换为十六进制。
例子
示例1:将2转换为十六进制。
1)首先将2转换为二进制补码:
2 (base 10) = 0000 0010 (base 2)
2)现在将二进制转换为十六进制:
0000 = 0x0 in hex
0010 = 0x2 in hex
therefore 2 = 0000 0010 = 0x02.
示例2:将-2(以二进制补码形式)转换为十六进制。
1)首先将-2换成二进制补码:
-2 (base 10) = 0000 0010 (direct conversion to binary)
1111 1101 (invert bits)
1111 1110 (add 1)
therefore: -2 = 1111 1110 (in two's complement)
2)现在转换为十六进制:
1111 = 0xF in hex
1110 = 0xE in hex
therefore: -2 = 1111 1110 = 0xFE.
在Java中这样做
现在我们已经介绍了这个概念,你会发现我们可以通过一些简单的掩饰和移位来实现我们想要的。 要理解的关键是你正试图转换的字节已经是二进制补码了。 你不要自己做这个转换。 我认为这是在这个问题上混淆的主要观点。 以下面的字节数组为例:
byte[] bytes = new byte[]{-2,2};
我们只是手动将它们转换为十六进制,但是我们如何在Java中执行它? 就是这样:
第1步:创建一个StringBuffer来保存我们的计算。
StringBuffer buffer = new StringBuffer();
第2步:隔离高阶位,将它们转换为十六进制,并将它们附加到缓冲区
给定二进制数1111 1110,我们可以通过先将它们移位4来隔离高位,然后清零剩下的数字。 从逻辑上讲,这很简单,但是,Java(以及许多语言)中的实现细节由于符号扩展而引入了皱纹。 实质上,当您移动一个字节值时,Java首先将您的值转换为整数,然后执行符号扩展。 因此,虽然您预计1111 1110 >> 4为0000 1111,但实际上,在Java中,它表示为二进制补码0xFFFFFFFF!
所以回到我们的例子:
1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two's complement)
然后我们可以用掩码来隔离这些位:
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111
therefore: 1111 = 0xF in hex.
在Java中,我们可以完成这一切:
Character.forDigit((bytes[0] >> 4) & 0xF, 16);
forDigit函数只是将您传递给它的数字映射到一组十六进制数字0-F。
第3步:接下来我们需要隔离低位。 由于我们想要的位已经处于正确的位置,我们可以将它们掩盖起来:
1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before)
therefore: 1110 = 0xE in hex.
像以前一样,在Java中,我们可以一下子做到这一点:
Character.forDigit((bytes[0] & 0xF), 16);
把它放在一起,我们可以做一个for循环并转换整个数组:
for(int i=0; i < bytes.length; i++){
buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
buffer.append(Character.forDigit((bytes[i] & 0xF), 16));
}
希望这个解释能让你更清楚地知道你会在互联网上找到的许多例子中发生了什么。 希望我没有犯任何令人震惊的错误,但建议和更正非常受欢迎!
我发现要做到这一点的最快方式如下:
private static final String HEXES = "0123456789ABCDEF";
static String getHex(byte[] raw) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
它比String.format
快大约50倍。 如果你想测试它:
public class MyTest{
private static final String HEXES = "0123456789ABCDEF";
@Test
public void test_get_hex() {
byte[] raw = {
(byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
(byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
(byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
(byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
(byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
(byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
(byte) 0xd6, (byte) 0x10,
};
int N = 77777;
long t;
{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 50
}
{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
StringBuilder hex = new StringBuilder(2 * raw.length);
for (byte b : raw) {
hex.append(String.format("%02X", b));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 2535
}
}
}
编辑 :刚刚发现了一个更快的东西,并保持在一条线上,但与JRE 9 不兼容 。 请自行承担风险
import javax.xml.bind.DatatypeConverter;
DatatypeConverter.printHexBinary(raw);
链接地址: http://www.djcxy.com/p/17683.html