【问题标题】:Can someone explain the conversion from byte array to hex string?有人可以解释从字节数组到十六进制字符串的转换吗?
【发布时间】:2010-11-05 19:25:15
【问题描述】:

我最近开始研究 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 中使用位移,所以我对此有点生疏。有人好心地说明(简单来说)上面的代码是如何进行转换的? >>>“?

我还在 StackOverflow 上找到了其他解决方案,例如 herehere,它们使用 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();
}

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

感谢您的宝贵时间。

【问题讨论】:

    标签: java md5 bytearray hex


    【解决方案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

    【讨论】:

      【解决方案2】:
      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 位向下移动(>>> 是无符号右移)并与 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 加到 '0' 字符以获得字符 '1',将其附加到字符串并继续前进.

                      halfbyte = data[i] & 0x0F;
      

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

      再一次,如果我们的输入是 23 ... 0001 0111,逻辑与之后就变成了 0000 0111,即十进制的 7。重复上述相同的逻辑,显示字符'7'。

                  } while(two_halfs++ < 1);
      

      现在我们只需移动到数组中的下一个字节并重复。

              }
          return buf.toString();
      }
      

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

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

      【讨论】:

      • +1 表示努力并击败了我。我唯一要添加的是对按位操作文档的引用:j2ee.me/docs/books/tutorial/java/nutsandbolts/op3.html
      • 非常感谢您的详细解释!
      • 当您将原始位向右移动 4 位时,您会从原始输入中获​​得 4 个零和前 4 个位。为什么在这种情况下我们需要执行 &0x0F - 我没有看到任何区别(最后 4 位始终保持不变)? &0x0F 的第二次执行对我来说很清楚。
      【解决方案3】:

      回答这个问题:

      为什么这也有效

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

      【讨论】:

      • 感谢您注意到差异。
      【解决方案4】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-24
        • 2013-01-14
        • 1970-01-01
        • 1970-01-01
        • 2017-06-17
        • 2014-03-17
        • 1970-01-01
        相关资源
        最近更新 更多