在 Java 中,如何将字节数组转换为十六进制数字字符串,同时保留前导零?
我正在使用一些示例 java 代码来生成 md5 哈希值。 其中一部分将结果从字节转换为十六进制数字字符串:
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
但是,它不太有效,因为 toHexString 显然会丢失前导零。 那么,从字节数组转换为保留前导零的十六进制字符串的最简单方法是什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(28)
这是一个错误的解决方案吗? (android java)
所以基本上它用 0 替换空格。
Is that a faulty solution? (android java)
So basically it replaces spaces with 0.
令我惊讶的是,没有人想出以下解决方案:
I'm surprised that no one came up with the following solution:
或者你可以这样做:
它简短、简单,基本上只是格式的改变。
Or you can do this:
Its Short, simple and basically just a format change.
这也是等效的,但使用 Apache util HexBin 其中代码简化为
This is also equivalent but more concise using Apache util HexBin where the code reduces to
查看 Apache Commons 编解码器的 ">Hex.encodeHexString。
Check out Hex.encodeHexString from Apache Commons Codec.
您可以使用下面的一个。 我使用前导零字节和初始负字节对此进行了测试。
如果您想要小写十六进制数字,请在格式字符串中使用
"x"
。You can use the one below. I tested this with leading zero bytes and with initial negative bytes as well
If you want lowercase hex digits, use
"x"
in the format String.一种简单的方法是检查 Integer.toHexString() 输出了多少位数字,并在需要时向每个字节添加前导零。 像这样的事情:
A simple approach would be to check how many digits are output by
Integer.toHexString()
and add a leading zero to each byte if needed. Something like this:该方法 < strong>
javax.xml.bind.DatatypeConverter.printHexBinary()
,Java XML 绑定架构 (JAXB) 是将byte[]
转换为十六进制字符串的便捷方法。DatatypeConverter
< /a> 类还包含许多其他有用的数据操作方法。在 Java 8 及更早版本中,JAXB 是 Java 标准库的一部分。 它是 ,Java 11 中已删除作为将所有 Java EE 包移至其自己的库中的努力的一部分。 说来话长< /a>. 现在,
javax.xml.bind
不存在,如果您想使用包含DatatypeConverter
的 JAXB,则需要安装 JAXB API 和 JAXB 运行时。用法示例:
将导致:
The method
javax.xml.bind.DatatypeConverter.printHexBinary()
, part of the Java Architecture for XML Binding (JAXB), was a convenient way to convert abyte[]
to a hex string. TheDatatypeConverter
class also included many other useful data-manipulation methods.In Java 8 and earlier, JAXB was part of the Java standard library. It was deprecated with Java 9 and removed with Java 11, as part of an effort to move all Java EE packages into their own libraries. It's a long story. Now,
javax.xml.bind
doesn't exist, and if you want to use JAXB, which containsDatatypeConverter
, you'll need to install the JAXB API and JAXB Runtime from Maven.Example usage:
Will result in:
我喜欢史蒂夫提交的内容,但他本来可以在没有几个变量的情况下完成,并在此过程中节省了几行。
我喜欢这一点的是,很容易准确地看到它在做什么(而不是依赖于一些神奇的 BigInteger 黑盒转换),而且您也不必担心像前导零之类的极端情况。 该例程获取每个 4 位半字节并将其转换为十六进制字符。 它使用表查找,所以速度可能很快。 如果用按位移位和 AND 替换 v/16 和 v%16 可能会更快,但我现在懒得测试它。
I liked Steve's submissions, but he could have done without a couple of variables and saved several lines in the process.
What I like about this is that it's easy to see exactly what it's doing (instead of relying on some magic BigInteger black box conversion) and you're also free from having to worry about corner cases like leading-zeroes and stuff. This routine takes every 4-bit nibble and turns it into a hex char. And it's using a table lookup, so it's probably fast. It could probably be faster if you replace v/16 and v%16 with bitwise shifts and AND's, but I'm too lazy to test it right now.
我发现 Integer.toHexString 有点慢。 如果要转换许多字节,您可能需要考虑构建一个包含“00”..“FF”的字符串数组,并使用整数作为索引。 即,
这更快并确保正确的长度。 只需要字符串数组:
I found Integer.toHexString to be a little slow. If you are converting many bytes, you may want to consider building an array of Strings containing "00".."FF" and use the integer as the index. I.e.
This is faster and ensures the correct length. Just requires the array of strings:
我一直在寻找同样的事情......这里有一些好主意,但我运行了一些微观基准测试。 我发现以下是最快的(从 Ayman 的上面修改而来,速度大约是 2 倍,比 Steve 的速度快 50%):
编辑:哎呀 - 错过了这本质上与 kgiannakakis 的相同,因此可能会被剥离前导 0。不过,将其修改为以下内容,它仍然是最快的:
I've been looking for the same thing ... some good ideas here, but I ran a few micro benchmarks. I found the following to be the fastest (modified from Ayman's above and about 2x as fast, and about 50% faster than Steve's just above this one):
Edit: Oops - missed that this is essentially the same as kgiannakakis's and so may strip off a leading 0. Still, modifying this to the following, it's still the fastest:
我会使用类似这样的固定长度,例如散列:
掩码中的
0
进行填充...I would use something like this for fixed length, like hashes:
The
0
in the mask does the padding...鉴于您已有的解决方案,这是最短的解决方案。 如果您可以将字节数组转换为数值,
String.format
可以同时将其转换为十六进制字符串。That's the shortest solution given what you already have. If you could convert the byte array to a numeric value,
String.format
can convert it to a hex string at the same time.Guava 也让它变得非常简单:
当 Apache Commons 运行时,这是一个不错的选择无法使用。 它还有一些很好的输出控制,例如:
Guava makes it pretty simple too:
It's a nice alternative when Apache Commons is not available. It also has some nice controls of the output like:
这个解决方案有点老派,并且应该具有内存效率。
This solution is a little older school, and should be memory efficient.
另外一个选择
Another option
为了保持前导零,这里是 Paul 建议的一个小变化(例如 md5 哈希):
哎呀,这看起来比 Ayman 提出的要差,对此感到抱歉
In order to keep leading zeroes, here is a small variation on what has Paul suggested (eg md5 hash):
Oops, this looks poorer than what's Ayman proposed, sorry for that
看起来 concat 和append 函数可能非常慢。 以下对我来说要快得多(比我之前的帖子)。 在构建输出时更改为字符数组是加快速度的关键因素。 我没有与 Brandon DuRette 建议的 Hex.encodeHex 进行比较。
It appears concat and append functions can be really slow. The following was MUCH faster for me (than my previous post). Changing to a char array in building the output was the key factor to speed it up. I have not compared to Hex.encodeHex suggested by Brandon DuRette.
这就是我用于 MD5 哈希值的内容:
编辑:我已经测试过了,我注意到这样尾随的零也被删除了。 但这只能发生在开始时,因此您可以与预期长度进行比较并相应地进行填充。
This what I am using for MD5 hashes:
EDIT: I've tested and I've noticed that with this also trailing zeros are cut. But this can only happen in the beginning, so you can compare with the expected length and pad accordingly.
您可以在没有外部库的情况下减少编写量:
You can get it writing less without external libraries:
该解决方案不需要位移或掩码、查找表或外部库,并且大约是我能得到的最短的:
This solution requires no bit-shifting or -masking, lookup tables, or external libraries, and is about as short as I can get:
恕我直言,上面所有提供删除前导零的片段的解决方案都是错误的。
根据此代码片段,从字节数组中取出 8 位
迭代,转换为整数(因为 Integer.toHexString 函数采用
int 作为参数),然后将该整数转换为相应的哈希值
价值。 因此,例如,如果您有二进制 00000001 00000001,根据
在代码中,hexString 变量的十六进制值将为 0x11,而
正确的值应该是0x0101。 因此,在计算 MD5 时我们可能会得到哈希值
长度<32字节(因为缺少零)可能不满足
MD5 哈希所具有的加密独特属性。
问题的解决方案是将上面的代码片段替换为
以下片段:
IMHO all the solutions above that provide snippets to remove the leading zeroes are wrong.
According to this snippet, 8 bits are taken from the byte array in an
iteration, converted into an integer (since Integer.toHexString function takes
int as argument) and then that integer is converted to the corresponding hash
value. So, for example if you have 00000001 00000001 in binary, according to
the code, the hexString variable would have 0x11 as the hex value whereas
correct value should be 0x0101. Thus, while calculating MD5 we may get hashes
of length <32 bytes(because of missing zeroes) which may not satisfy the
cryptographically unique properties that MD5 hash does.
The solution to the problem is replacing the above code snippet by the
following snippet:
这将为一个字节提供两个字符长的字符串。
This will give two-char long string for a byte.
如何再次从 ascii 转换回字节数组?
我按照 Jemenake 给出的以下代码转换为 ascii。
And how can you convert back again from ascii to byte array ?
i followed following code to convert to ascii given by Jemenake.
我的变体
它对我有用。
my variant
it works for me.