关于BytEray:如何将字节数组转换成Java中的十六进制字符串?

How to convert a byte array to a hex string in Java?

我用十六进制数字填充了一个字节数组,并且以简单的方式打印它是毫无意义的,因为有许多不可打印的元素。我需要的是:3a5f771c形式的确切十六进制代码


从这里的讨论,特别是这个答案,这是我目前使用的功能:

1
2
3
4
5
6
7
8
9
10
private final static char[] hexArray ="0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for ( int j = 0; j < bytes.length; j++ ) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

我自己的小基准测试(一百万字节1000倍,256字节1000万倍)显示它比任何其他替代方法都快,大约是长数组的一半。与我从中得到的答案相比,切换到位操作(如讨论中所建议的)可以为长数组节省大约20%的时间。(编辑:当我说它比备选方案更快时,我是指讨论中提供的备选方案代码。性能相当于通用编解码器,它使用非常相似的代码。)


ApacheCommons编解码器库有一个十六进制类,用于执行这种类型的工作。

1
2
3
4
5
import org.apache.commons.codec.binary.Hex;

String foo ="I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );


使用DatatypeConverter.printHexBinary()。您可以在http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/datatypeconverter.html中阅读其文档。

例如:

1
2
byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(bytes));

将导致:

1
000086003D

如您所见,这将检索十六进制字符串,该字符串表示带前导零的字节数组。

这个答案与Java中的问题基本相同,如何在保持前导零的同时,将字节数组转换为十六进制数字串?


最简单的解决方案,无外部libs,无数字常量:

1
2
3
4
5
6
public static String byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}


一个番石榴解决方案,完整性:

1
2
3
4
import com.google.common.io.BaseEncoding;
...
byte[] bytes ="Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);

现在,hex"48656c6c6f20776f726c64"


这个简单的一行程序适合我String result = new BigInteger(1, inputBytes).toString(16); 编辑-使用这个将删除前导零,但是Hey为我的用例工作。谢谢@voicu的指点


使用数据类型转换器Classjavax.xml.bind.DataTypeConverter

String hexString = DatatypeConverter.printHexBinary(bytes[] raw);


以存储查找表的较小成本,此实现既简单又快速。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 private static final char[] BYTE2HEX=(
   "000102030405060708090A0B0C0D0E0F"+
   "101112131415161718191A1B1C1D1E1F"+
   "202122232425262728292A2B2C2D2E2F"+
   "303132333435363738393A3B3C3D3E3F"+
   "404142434445464748494A4B4C4D4E4F"+
   "505152535455565758595A5B5C5D5E5F"+
   "606162636465666768696A6B6C6D6E6F"+
   "707172737475767778797A7B7C7D7E7F"+
   "808182838485868788898A8B8C8D8E8F"+
   "909192939495969798999A9B9C9D9E9F"+
   "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
   "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
   "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
   "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
   "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
   "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
   ;

  public static String getHexString(byte[] bytes) {
    final int len=bytes.length;
    final char[] chars=new char[len<<1];
    int hexIndex;
    int idx=0;
    int ofs=0;
    while (ofs<len) {
      hexIndex=(bytes[ofs++] & 0xFF)<<1;
      chars[idx++]=BYTE2HEX[hexIndex++];
      chars[idx++]=BYTE2HEX[hexIndex];
    }
    return new String(chars);
  }


对于固定长度,我将使用类似这样的方法,例如哈希:

1
md5sum = String.format("%032x", new BigInteger(1, md.digest()));


我发现这里有三种不同的方式:网址:http://www.rgagnon.com/javadetails/java-0596.html

正如他所指出的,最优雅的是这个:

1
2
3
4
5
6
7
8
9
10
11
12
static final String HEXES ="0123456789ABCDEF";
public static String getHex( byte [] raw ) {
    if ( raw == null ) {
        return null;
    }
    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4))
            .append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}


这个怎么样?

1
2
3
4
5
6
7
8
9
10
11
    String byteToHex(final byte[] hash)
    {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

好的,有很多方法可以做到这一点,但是如果你决定使用一个库,我建议在你的项目中搜索一下,看看在添加一个新的库之前,这个库已经是你项目的一部分了。例如,如果你还没有

org.apache.commons.codec.binary.Hex

也许你真的…

org.apache.xerces.impl.dv.util.HexBin


我通常对debuf语句使用以下方法,但我不知道这是不是最好的方法

1
2
3
4
5
6
7
8
9
10
11
12
private static String digits ="0123456789abcdef";

public static String toHex(byte[] data){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i != data.length; i++)
    {
        int v = data[i] & 0xff;
        buf.append(digits.charAt(v >> 4));
        buf.append(digits.charAt(v & 0xf));
    }
    return buf.toString();
}


我喜欢用这个:

1
2
3
4
5
6
7
8
9
10
final protected static char[] hexArray ="0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes, int offset, int count) {
    char[] hexChars = new char[count * 2];
    for ( int j = 0; j < count; j++ ) {
        int v = bytes[j+offset] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

它对公认答案的适应性稍有灵活性。就我个人而言,我既保留了公认的答案,也保留了这种超负荷的答案,在更多的上下文中都可以使用。


@maybewecouldsealavan提出的解决方案的一个小变种,它允许您在输出十六进制字符串中直观地将n个字节捆绑在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 final static char[] HEX_ARRAY ="0123456789ABCDEF".toCharArray();
 final static char BUNDLE_SEP = ' ';

public static String bytesToHexString(byte[] bytes, int bundleSize /*[bytes]*/]) {
        char[] hexChars = new char[(bytes.length * 2) + (bytes.length / bundleSize)];
        for (int j = 0, k = 1; j < bytes.length; j++, k++) {
                int v = bytes[j] & 0xFF;
                int start = (j * 2) + j/bundleSize;

                hexChars[start] = HEX_ARRAY[v >>> 4];
                hexChars[start + 1] = HEX_ARRAY[v & 0x0F];

                if ((k % bundleSize) == 0) {
                        hexChars[start + 2] = BUNDLE_SEP;
                }  
        }  
        return new String(hexChars).trim();    
}

即:

1
2
3
4
5
bytesToHexString("..DOOM..".toCharArray().getBytes(), 2);
2E2E 444F 4F4D 2E2E

bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E

如果您使用的是Spring安全框架,那么可以使用:

1
2
3
4
5
import org.springframework.security.crypto.codec.Hex

final String testString ="Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));

在该页上找不到任何没有

  • 使用循环
  • 使用javax.xml.bind.datatypeconverter,它可以很好地编译,但经常在运行时抛出java.lang.noclassDefoundError。
  • 这里有一个解决方案,它没有上述缺陷(但我的承诺没有其他缺陷)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import java.math.BigInteger;

    import static java.lang.System.out;
    public final class App2 {
        // | proposed solution.
        public static String encode(byte[] bytes) {          
            final int length = bytes.length;

            // | BigInteger constructor throws if it is given an empty array.
            if (length == 0) {
                return"00";
            }

            final int evenLength = (int)(2 * Math.ceil(length / 2.0));
            final String format ="%0" + evenLength +"x";        
            final String result = String.format (format, new BigInteger(bytes));

            return result;
        }

        public static void main(String[] args) throws Exception {
            // 00
            out.println(encode(new byte[] {}));

            // 01
            out.println(encode(new byte[] {1}));

            //203040
            out.println(encode(new byte[] {0x20, 0x30, 0x40}));

            // 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e
            out.println(encode("All your base are belong to us.".getBytes()));
        }
    }

    我无法在62个操作码下得到它,但是如果在第一个字节小于0x10的情况下,您可以不使用0填充,那么下面的解决方案只使用23个操作码。如果本机实现还不可用(或者在本例中,如果BigInteger在ToString中有一个前缀为零的选项),那么"易于实现"的解决方案(如"字符串长度为奇数时使用零填充")将变得非常昂贵。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static String encode(byte[] bytes) {          
        final int length = bytes.length;

        // | BigInteger constructor throws if it is given an empty array.
        if (length == 0) {
            return"00";
        }

        return new BigInteger(bytes).toString(16);
    }

    我的解决方案是基于Maybwecouldstealavan的解决方案,但不依赖任何额外分配的查找表。它不使用任何"int-to-char"的casts-hack(实际上,Character.forDigit()是这样做的,执行一些比较以检查数字的真实性),因此可能会慢一些。请随意使用。干杯。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public static String bytesToHex(final byte[] bytes)
    {
        final int numBytes = bytes.length;
        final char[] container = new char[numBytes * 2];

        for (int i = 0; i < numBytes; i++)
        {
            final int b = bytes[i] & 0xFF;

            container[i * 2] = Character.forDigit(b >>> 4, 0x10);
            container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
        }

        return new String(container);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
      public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
          data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
            + Character.digit(s.charAt(i+1), 16));
        }
      return data;
      }


    //移位字节更有效//你也可以用这个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static String getHexString (String s)
    {
        byte[] buf = s.getBytes();

        StringBuffer sb = new StringBuffer();

        for (byte b:buf)
        {
            sb.append(String.format("%x", b));
        }


            return sb.toString();
    }

    这是一个类似于java.util.Base64的实现(部分实现),不是很漂亮吗?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Base16/*a.k.a. Hex*/ {
        public static class Encoder{
            private static char[] toLowerHex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
            private static char[] toUpperHex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            private boolean upper;
            public Encoder(boolean upper) {
                this.upper=upper;
            }
            public String encode(byte[] data){
                char[] value=new char[data.length*2];
                char[] toHex=upper?toUpperHex:toLowerHex;
                for(int i=0,j=0;i<data.length;i++){
                    int octet=data[i]&0xFF;
                    value[j++]=toHex[octet>>4];
                    value[j++]=toHex[octet&0xF];
                }
                return new String(value);
            }
            static final Encoder LOWER=new Encoder(false);
            static final Encoder UPPER=new Encoder(true);
        }
        public static Encoder getEncoder(){
            return Encoder.LOWER;
        }
        public static Encoder getUpperEncoder(){
            return Encoder.UPPER;
        }
        //...
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private static String bytesToHexString(byte[] bytes, int length) {
            if (bytes == null || length == 0) return null;

            StringBuilder ret = new StringBuilder(2*length);

            for (int i = 0 ; i < length ; i++) {
                int b;

                b = 0x0f & (bytes[i] >> 4);
                ret.append("0123456789abcdef".charAt(b));

                b = 0x0f & bytes[i];
                ret.append("0123456789abcdef".charAt(b));
            }

            return ret.toString();
        }

    如果您正在寻找一个与Python完全一样的字节数组,那么我将Java实现转换为Python。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class ByteArray:

    @classmethod
    def char(cls, args=[]):
        cls.hexArray ="0123456789ABCDEF".encode('utf-16')
        j = 0
        length = (cls.hexArray)

        if j < length:
            v = j & 0xFF
            hexChars = [None, None]
            hexChars[j * 2] = str( cls.hexArray) + str(v)
            hexChars[j * 2 + 1] = str(cls.hexArray) + str(v) + str(0x0F)
            # Use if you want...
            #hexChars.pop()

        return str(hexChars)

    array = ByteArray()
    print array.char(args=[])