我有一个字节数组。 我希望将该数组的每个字节String转换为相应的十六进制值。

Java中是否有将字节数组转换为十六进制的函数?


当前回答

只是添加我的两分,因为我看到许多答案使用字符数组和/或使用StringBuilder实例,并声称是快速或更快。

由于我使用ASCII表组织有一个不同的想法,代码点48-57为0-9,代码点65-70为a-f,代码点97-102为a-f,我想测试哪个想法是最快的。

因为我已经好几年没有做过类似的事情了,所以我要做一个广泛的拍摄。我在不同大小的数组中使用了10亿字节(1M, 1K, 10),因此每个数组有1000倍1M字节,每个数组有1M倍1000字节,每个数组有100M倍10字节。

结果是1-F中的char数组胜出。使用char数组而不是StringBuilder作为输出也很容易(对象更少,不需要测试容量,在增长时不需要新数组或复制)。此外,当使用foreach (for(var b: bytes))循环时,你似乎会得到一个小的惩罚。

使用我的想法的版本大约是。每个数组1M字节时慢15%,每个数组1000字节时慢21%,每个数组10字节时慢18%。StringBuilder版本分别慢了210%、380%和310%。

这很糟糕,但也不是那么出乎意料,因为在第一级缓存中查找小数组胜过if和add... .(一个缓存访问+偏移量计算vs.一个if,一个跳转,一个add ->不确定跳转thou)。

我的版本:

public static String bytesToHex(byte [] bytes) {
    
    char [] result = new char [bytes.length * 2];
    
    for(int index = 0; index < bytes.length; index++) {
        int v = bytes[index]; 

        int upper = (v >>> 4) & 0xF;
        result[index * 2] = (char)(upper + (upper < 10 ? 48 : 65 - 10));
        
        int lower = v & 0xF;
        result[index * 2 + 1] = (char)(lower + (lower < 10 ? 48 : 65 - 10));
    }
    
    return new String(result);
}

PS:是的,我做了多次测试,每次都做了最好的测试,做了热身,也做了100亿字符的测试,以确保相同的图片... .

其他回答

    byte[] bytes = {-1, 0, 1, 2, 3 };
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02X ", b));
    }
    System.out.println(sb.toString());
    // prints "FF 00 01 02 03 "

另请参阅

java.util.Formatter语法 %(旗帜)(宽度)转换 标记'0' -结果将被填充为零 宽度2 转换'X' -结果被格式化为十六进制整数,大写


看看问题的文本,也有可能是这样要求的:

    String[] arr = {"-1", "0", "10", "20" };
    for (int i = 0; i < arr.length; i++) {
        arr[i] = String.format("%02x", Byte.parseByte(arr[i]));
    }
    System.out.println(java.util.Arrays.toString(arr));
    // prints "[ff, 00, 0a, 14]"

这里的几个答案使用Integer.toHexString(int);这是可行的,但有一些注意事项。由于形参是int型,因此对byte参数执行扩大原语转换,这涉及到符号扩展。

    byte b = -1;
    System.out.println(Integer.toHexString(b));
    // prints "ffffffff"

在Java中有符号的8位字节被符号扩展为32位整型。为了有效地撤销这个符号扩展,可以使用0xFF来屏蔽字节。

    byte b = -1;
    System.out.println(Integer.toHexString(b & 0xFF));
    // prints "ff"

使用toHexString的另一个问题是它不会用零填充:

    byte b = 10;
    System.out.println(Integer.toHexString(b & 0xFF));
    // prints "a"

这两个因素结合起来应该形成字符串。格式解决方案更佳。

参考文献

整型类型和值 对于字节,从-128到127,包括 JLS 5.1.2扩大原语转换

其他人已经报道了一般情况。但是如果你有一个已知形式的字节数组,例如MAC地址,那么你可以:

byte[] mac = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };

String str = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
                           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 

你可以使用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>

下面是一个将字节转换为十六进制的简单函数

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

如果你想要一个恒定宽度的十六进制表示,即0A而不是a,这样你就可以明确地恢复字节,尝试format():

StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
    result.append(String.format("%02X", bb));
}
return result.toString();