有人可以解释从字节数组到十六进制字符串的转换吗?
我最近开始研究 MD5 散列(在 Java 中),虽然我找到了可以帮助我实现这一目标的算法和方法,但我仍然想知道它实际上是如何工作的。
首先,我从 此 URL 中找到了以下内容:
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
我还没有找到在 Java 中是否需要使用位移位,所以我对此有点生疏。 有人好心地解释一下(简单地说)上面的代码到底是如何进行转换的? “>>”?
我还在 StackOverflow 上找到了其他解决方案,例如此处和此处,它使用 BigInteger 代替:
try {
String s = "TEST STRING";
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(s.getBytes(),0,s.length());
String signature = new BigInteger(1,md5.digest()).toString(16);
System.out.println("Signature: "+signature);
} catch (final NoSuchAlgorithmException e) {
e.printStackTrace();
}
为什么这也有效,哪种方式更有效高效的?
谢谢你的时间。
I recently started looking at MD5 hashing (in Java) and while I've found algorithms and methods to help me accomplish that, I'm left wondering how it actually works.
For one, I found the following from this URL:
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while(two_halfs++ < 1);
}
return buf.toString();
}
I haven't found any need to use bit-shifting in Java so I'm a bit rusty on that. Someone kind enough to illustrate (in simple terms) how exactly does the above code does the conversion? ">>>"?
I also found other solutions on StackOverflow, such as here and here, which uses BigInteger instead:
try {
String s = "TEST STRING";
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(s.getBytes(),0,s.length());
String signature = new BigInteger(1,md5.digest()).toString(16);
System.out.println("Signature: "+signature);
} catch (final NoSuchAlgorithmException e) {
e.printStackTrace();
}
Why does that work too, and which way is more efficient?
Thanks for your time.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这些东西你不必自己写,因为它已经写在 apache-commons-codec 中:
Hex
类中有很多更有用的方法。These stuff you don't have to write by yourself, because it is already written in apache-commons-codec:
There are a lot of more useful methods in
Hex
class.到目前为止...只是基本设置并启动一个循环来遍历数组中的所有字节,
当转换为十六进制时,字节是两个十六进制数字或 8 个二进制数字,具体取决于您查看的基数。上面的语句发生了变化高 4 位向下(>> 是无符号右移)并将其与 0000 1111 进行逻辑与,以便结果是等于字节的高 4 位(第一个十六进制数字)的整数。
假设 23 是一个输入,那么二进制就是 0001 0111。 移位使逻辑与将其转换为 0000 0001。
这只是将 do/while 循环设置为运行两次
这里我们显示实际的十六进制数字,基本上只是使用零或字符作为起点并向上移动到正确的字符。 第一个 if 语句涵盖了所有数字 0-9,第二个 if 语句涵盖了所有数字 10-15(十六进制的 af)。
同样,使用我们的示例 0000 0001 十进制等于 1。我们陷入了上面的 if 块并添加1 到“0”字符以获取字符“1”,将其附加到字符串并继续。
现在我们将整数设置为恰好等于字节的低位并重复。
同样,如果我们的输入是 23 ... 0001 0111,逻辑 AND 后就变成 0000 0111,即十进制的 7。 重复上述相同的逻辑,显示字符“7”。
现在我们只需移至数组中的下一个字节并重复。
为了回答您的下一个问题,Java API 已经在 BigInteger 中内置了一个基本转换实用程序。 请参阅 toString(int radix ) 文档。
由于不知道 Java API 使用的实现,我不能肯定地说,但我愿意打赌 Java 实现比您发布的第一个稍微简单的算法更有效。
Up till this point ... just basic set up and starting a loop to go through all bytes in the array
bytes when converted to hex are two hex digits or 8 binary digits depending on what base you look at it in. The above statement shifts the high 4 bits down (>>> is unsigned right shift) and logical ANDs it with 0000 1111 so that the result is an integer equal to the high 4 bits of the byte (first hex digit).
Say 23 was an input, this is 0001 0111 in binary. The shift makes and logical AND coverts this to 0000 0001.
This just sets up the do/while loop to run twice
Here we're displaying the actual hex digit, basically just using the zero or a character as a starting point and shifting up to the correct character. The first if statement covers all the digits 0-9, and the second covers all digits 10-15 (a-f in hex)
Again, using our example 0000 0001 in decimal is equal to 1. We get caught in the upper if block and add 1 to the '0' character to get the character '1', append that to the string and move on.
Now we set up the integer to just equal the low bits from the byte and repeat.
Again, if our input was 23 ... 0001 0111 after the logical AND becomes just 0000 0111 which is 7 in decimal. Repeat the same logic as above and the character '7' is displayed.
Now we just move on to the next byte in the array and repeat.
To answer your next question, the Java API already has a base conversion utility built in to BigInteger already. See the toString(int radix) documentation.
Not knowing the implementation used by the Java API, I can't say for sure, but I'd be willing to bet that the Java implenentation is more efficient than the first somewhat simple algorithm you posted.
有关位移位的完整解释请查看以下问题的答案
什么是按位移位 (bit-shift) 运算符以及它们如何工作?
他似乎试图将一个字节转换为小于 16 的数字,通过这样做,他可以轻松地确定该字节用代码代表哪个字符
这是一个简单的答案,但我无论如何都不那么聪明=D
For a thorough explanation on bitshifting check out the answers in the following SO question
What are bitwise shift (bit-shift) operators and how do they work?
He seems to try to convert one single byte into a number smaller than 16, by doing so he can easily determine wich caracther that byte represents with the code
This is a simplistic answer, but im not that bright anyhow =D
回答这个问题:
?它不起作用。 至少,与循环版本的方式不同。 new BigInteger(...).toString(16) 不会显示前导零,而前一个版本会显示前导零。 通常,对于写出字节数组(尤其是表示哈希之类的数组)之类的内容,您会需要固定长度的输出,因此如果您想使用该版本,则必须适当地填充它。
To answer this bit:
It doesn't. At least, not the same way that the loop version does. new BigInteger(...).toString(16) will not show leading zeroes, which the former version will. Usually for something like writing out a byte array (especially one representing something like a hash) you would want a fixed-length output so if you want to use that version you'd have to pad it out appropriately.