我有一个字节数组。 我希望将该数组的每个字节String转换为相应的十六进制值。
Java中是否有将字节数组转换为十六进制的函数?
我有一个字节数组。 我希望将该数组的每个字节String转换为相应的十六进制值。
Java中是否有将字节数组转换为十六进制的函数?
当前回答
如果你想要一个恒定宽度的十六进制表示,即0A而不是a,这样你就可以明确地恢复字节,尝试format():
StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
result.append(String.format("%02X", bb));
}
return result.toString();
其他回答
就像其他答案一样,我建议使用String.format()和BigInteger。但是要将字节数组解释为大端二进制表示,而不是双补二进制表示(使用signum和不完全使用可能的十六进制值范围),请使用BigInteger(int signum, byte[] magnitude),而不是BigInteger(byte[] val)。
例如,对于长度为8的字节数组,使用:
String.format("%016X", new BigInteger(1,bytes))
优点:
前导零 没有符号 只有内置函数 只有一行代码
劣势:
也许有更有效的方法
例子:
byte[] bytes = new byte[8];
Random r = new Random();
System.out.println("big-endian | two's-complement");
System.out.println("-----------------|-----------------");
for (int i = 0; i < 10; i++) {
r.nextBytes(bytes);
System.out.print(String.format("%016X", new BigInteger(1,bytes)));
System.out.print(" | ");
System.out.print(String.format("%016X", new BigInteger(bytes)));
System.out.println();
}
示例输出:
big-endian | two's-complement
-----------------|-----------------
3971B56BC7C80590 | 3971B56BC7C80590
64D3C133C86CCBDC | 64D3C133C86CCBDC
B232EFD5BC40FA61 | -4DCD102A43BF059F
CD350CC7DF7C9731 | -32CAF338208368CF
82CDC9ECC1BC8EED | -7D3236133E437113
F438C8C34911A7F5 | -BC7373CB6EE580B
5E99738BE6ACE798 | 5E99738BE6ACE798
A565FE5CE43AA8DD | -5A9A01A31BC55723
032EBA783D2E9A9F | 032EBA783D2E9A9F
8FDAA07263217ABA | -70255F8D9CDE8546
如果您乐于使用外部库,则org.apache.commons.codec.binary.Hex类有一个encodeHex方法,该方法接受一个字节[]并返回一个char[]。这个方法比format选项快得多,并且封装了转换的细节。还附带了一个decodeHex方法,用于相反的转换。
你可以使用Bouncy Castle Provider库中的方法:
org.bouncycastle.util.encoders.Hex.toHexString(byteArray);
Bouncy Castle Crypto包是一个Java实现 加密算法。这个jar包含JCE提供程序和 用于JDK 1.5的Bouncy Castle Cryptography API的轻量级API JDK 1.8。
Maven的依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
或来自Apache Commons Codec:
org.apache.commons.codec.binary.Hex.encodeHexString(byteArray);
Apache Commons Codec包包含简单的编码器和解码器 用于各种格式,如Base64和十六进制。除了 这些编码器和解码器被广泛使用,编解码器包也有 维护语音编码实用程序的集合。
Maven的依赖:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
试试这个方法:
byte bv = 10;
String hexString = Integer.toHexString(bv);
处理数组(如果我没理解错的话):
byte[] bytes = {9, 10, 11, 15, 16};
StringBuffer result = new StringBuffer();
for (byte b : bytes) {
result.append(String.format("%02X ", b));
result.append(" "); // delimiter
}
return result.toString();
正如polygeneluants所提到的,String.format()是与Integer.toHexString()相比的正确答案(因为它以正确的方式处理负数)。
我在这里发帖是因为现有的答案都没有解释为什么他们的方法有效,我认为这对这个问题真的很重要。在某些情况下,这会导致所提出的解决方案显得不必要的复杂和微妙。为了说明这一点,我将提供一个相当简单的方法,但我将提供更多细节来帮助说明为什么它是有效的。
首先,我们要做什么?我们希望将字节值(或字节数组)转换为ASCII中表示十六进制值的字符串。所以第一步是找出Java中的字节是什么:
字节数据类型是一个8位有符号二补整数。最小值为-128,最大值为127(包括)。在大型数组中,字节数据类型对于节省内存非常有用,因为节省内存实际上很重要。它们也可以用来代替int,它们的限制有助于澄清你的代码;变量的范围是有限的这一事实可以作为一种文档形式。
What does this mean? A few things: First and most importantly, it means we are working with 8-bits. So for example we can write the number 2 as 0000 0010. However, since it is two's complement, we write a negative 2 like this: 1111 1110. What is also means is that converting to hex is very straightforward. That is, you simply convert each 4 bit segment directly to hex. Note that to make sense of negative numbers in this scheme you will first need to understand two's complement. If you don't already understand two's complement, you can read an excellent explanation, here: http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
将二的补数转换为十六进制
一旦一个数是二的补数,那么把它转换成十六进制就非常简单了。一般来说,从二进制转换为十六进制是非常简单的,正如您将在接下来的两个示例中看到的,您可以直接从2的补码转换为十六进制。
例子
例1:将2转换为十六进制。
1)首先将2转换为二进制的2的补码:
2 (base 10) = 0000 0010 (base 2)
2)现在将二进制转换为十六进制:
0000 = 0x0 in hex
0010 = 0x2 in hex
therefore 2 = 0000 0010 = 0x02.
例2:将-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中执行此操作
现在我们已经介绍了这个概念,您将发现我们可以通过一些简单的屏蔽和移动实现我们想要的效果。要理解的关键是,您试图转换的字节已经是2的补码。你不用自己做这个转换。我认为这是这个问题的一个主要混淆点。以下面的字节数组为例:
byte[] bytes = new byte[]{-2,2};
我们只是手动将它们转换为十六进制,但我们如何在Java中做到这一点呢?方法如下:
步骤1:创建一个StringBuffer来保存我们的计算。
StringBuffer buffer = new StringBuffer();
步骤2:隔离高阶位,将它们转换为十六进制,并将它们附加到缓冲区中
Given the binary number 1111 1110, we can isolate the higher order bits by first shifting them over by 4, and then zeroing out the rest of the number. Logically this is simple, however, the implementation details in Java (and many languages) introduce a wrinkle because of sign extension. Essentially, when you shift a byte value, Java first converts your value to an integer, and then performs sign extension. So while you would expect 1111 1110 >> 4 to be 0000 1111, in reality, in Java it is represented as the two's complement 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));
}
希望这个解释能让那些想知道在网上找到的许多例子中到底发生了什么的人更清楚。希望我没有犯任何严重的错误,但建议和纠正是非常欢迎的!