有人能解释从字节数组到十六进制字符串的转换吗?

时间:2009-06-25 10:56:52

标签: java md5 bytearray hex

我最近开始研究MD5哈希(用Java),虽然我找到了帮助我实现这一目标的算法和方法,但我还是想知道它是如何工作的。

首先,我从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();
}

我还没有发现在Java中使用位移的任何需要,所以我对此有点生疏。有人足以说明(简单来说)上述代码究竟是如何进行转换的? “&GT;&GT;&gt;” 中?

我还在StackOverflow上找到了其他解决方案,例如使用BigInteger的herehere

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();
}

为什么这样做也有效,哪种方式更有效?

感谢您的时间。

4 个答案:

答案 0 :(得分:10)

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {

到目前为止......只是基本设置并开始循环以遍历数组中的所有字节

        int halfbyte = (data[i] >>> 4) & 0x0F;
转换为十六进制时的

字节是两个十六进制数字或8个二进制数字,具体取决于您查看它的基数。上述语句将高4位向下移位(&gt;&gt;&gt;是无符号右移)和逻辑AND使用0000 1111,结果是一个等于字节高4位的整数(第一个十六进制数字)。

说23是输入,这是二进制的0001 0111。移位使得逻辑AND将其转换为0000 0001。

        int two_halfs = 0;
        do {

这只是将do / while循环设置为运行两次

            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

这里我们显示的是实际的十六进制数字,基本上只是使用零或一个字符作为起点并向上移动到正确的字符。第一个if语句覆盖所有数字0-9,第二个覆盖所有数字10-15(a-f,十六进制)

再一次,使用我们的十进制0000 0001示例等于1.我们陷入上面的if块并将'1'加1以获得字符'1',将其附加到字符串并继续

                halfbyte = data[i] & 0x0F;

现在我们将整数设置为恰好等于字节中的低位并重复。

同样,如果我们的输入是23 ... 0001 0111,则逻辑AND变为0000 0111,即十进制7。重复上述相同的逻辑,显示字符“7”。

            } while(two_halfs++ < 1);

现在我们继续前进到数组中的下一个字节并重复。

        }
    return buf.toString();
}

为了回答您的下一个问题,Java API已经在BigInteger中内置了一个基本转换实用程序。请参阅toString(int radix)文档。

我不知道Java API使用的实现,我不能肯定地说,但我愿意打赌,Java实现比你发布的第一个简单算法更有效。

答案 1 :(得分:2)

回答这个问题:

  

为什么这样做呢

没有。至少,与循环版本不同。 new BigInteger(...)。toString(16)将不会显示前一版本的前导零。通常用于写出一个字节数组(特别是一个表示类似哈希的数组),你会想要一个固定长度的输出,所以如果你想使用那个版本,你必须适当地填充它。

答案 2 :(得分:1)

有关位移的详细说明请查看以下SO问题中的答案 What are bitwise shift (bit-shift) operators and how do they work?

他似乎试图将一个字节转换为一个小于16的数字,这样他就可以很容易地确定该字节用代码表示的那个字符

  if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

这是一个简单的答案,但我不是那么明亮= D

答案 3 :(得分:0)

这些东西你不必自己写,因为它已经用apache-commons-codec编写了:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(byte[] array)

Hex类中有许多更有用的方法。