如何在Java中将字节数组转换为十六进制字符串?

And*_*dre 606 java hex bytearray

我有一个字节数组填充十六进制数字和打印它简单的方法是非常没有意义的,因为有许多不可打印的元素.我需要的是以下形式的确切十六进制代码:3a5f771c

may*_*Van 854

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

private static final char[] HEX_ARRAY = "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] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
Run Code Online (Sandbox Code Playgroud)

我自己的小基准测试(一百万字节一千万次,256字节一千万次)显示它比任何其他替代方案快得多,大约是长阵列的一半时间.与我从中得到的答案相比,切换到按位运算 - 如讨论中所建议的那样 - 为长阵列削减了大约20%的时间.(编辑:当我说它比替代品更快时,我的意思是讨论中提供的替代代码.性能等同于Commons Codec,它使用非常相似的代码.)

  • 我刚刚找到[javax.xml.bind.DataTypeConverter](http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html),这是标准发行版的一部分.当谷歌出现这种问题时,为什么不出现这种情况呢?很多有用的工具,包括`String printHexBinary(byte [])`和`byte [] parseHexBinary(String)`.然而,'printHexBinary`比这个答案中的函数慢很多(2x).(我检查了源代码;它使用`stringBuilder`.'parseHexBinary`使用数组.)但是,实际上,它足够快,你可能已经拥有了它. (256认同)
  • 答案为+1,因为Android没有DataTypeConverter (73认同)
  • 正在从Java 11中删除`javax.xml.bind.DataTypeConverter`. (15认同)
  • @maybeWeCouldStealAVan:JDK 7现在是开源的.我们应该提交一个补丁来提高`printHexBinary`的性能? (7认同)
  • @ bpbhat77反过来,请参阅:http://stackoverflow.com/a/140861/1284661 (6认同)
  • @AlbAtNf Java 只有带符号的数字原语,因此您想象为 0xFF 的字节将被视为负数。数组索引始终是 int,并且仅使用字节变量作为索引位置将与该方法通过将其分配给 `int v` 所做的相同:负字节将被扩展 - int 将充满设置位。你不会要求数组索引 15,而是数百万。 (4认同)
  • @maybeWeCouldStealAVan你可以解释一下这是如何工作的.我大部分都遵循,但非常喜欢了解使用代码时发生的事情.谢谢! (3认同)
  • 这段代码超级快!!我使用的是 `String.format("%0" + (bytes.length &lt;&lt; 1) + "X", new BigInteger(1, bytes));` 但性能很糟糕。这是一个更好的解决方案。 (2认同)
  • 为什么在这行`int v = bytes [j]&0xFF;`中需要`&0xFF`?我是否缺少某些东西,还是只是不必要? (2认同)

cho*_*ban 391

阿帕奇共享编解码器库有一个十六进制这样做只是这种类型的工作类.

import org.apache.commons.codec.binary.Hex;

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

  • 我强烈建议将这个答案换成最佳答案.总是投票使用经过良好测试的,高性能的开源库而不是自定义代码. (19认同)
  • @cytinus - 我的downvote发生在4个月前所以我不完全确定我的想法,但我可能反对图书馆的大小.这是程序中的一个小功能; 没有必要在项目中添加如此庞大的库来执行它. (12认同)
  • @ArtOfWarfare我不同意.唯一可怕的是,默认情况下,apache commons库不包含在JRE和JDK中.有一些库非常有用,默认情况下它们应该在您的类路径上,这就是其中之一. (10认同)
  • @ArtOfWarefare我同意,所以你可以做`import org.apache.com. (6认同)
  • 或者如果您使用BouncyCastle(_org.bouncycastle:bcprov-jdk15on_),您可以使用此类:`org.bouncycastle.util.encoders.Hex`,使用此方法:`String toHexString(byte [] data)` (6认同)
  • 另外,`commons-codec-1.9.jar`是258 KiB,如果您要使用Proguard或类似的东西,我想空间影响可能会进一步下降. (2认同)
  • 不要虚假地重振旧线程,而是作为未来参考的附加文档:即使为一些非常大的特定公司工作,Apache许可证也被认为可以在我们的商业产品中使用.恕我直言,如果你不能在这样的情况下快速投入/管理项目中的第三方依赖项,那么你的移动速度不够快. (2认同)

Pho*_*ixS 312

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

例如:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);
Run Code Online (Sandbox Code Playgroud)

将导致:

000086003D
Run Code Online (Sandbox Code Playgroud)

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

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

  • 一个很好的解决方案,但遗憾的是不是在Android中有效的解决方案. (11认同)
  • 从JDK 9开始,不再可以访问DatatypeConverter (10认同)
  • @PhoneixS它仍然存在,但不是默认运行时的一部分(由于Java 9模块). (2认同)
  • 不依赖于javax.xml.bind,它编译得很好但是在运行时找不到.如果你这样做,请准备好处理java.lang.NoClassDefFoundError (2认同)

Poi*_*ull 211

最简单的解决方案,没有外部库,没有数字常量:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • 如果它很慢,那又怎样?在我的用例中,它仅用于调试语句,所以感谢这个代码片段. (24认同)
  • 这非常慢,平均比顶部响应慢1000倍(长度为162字节).如果性能很重要,请避免使用String.Format. (13认同)
  • 也许慢.这对偶尔发生的事情很有好处,例如登录或类似事件. (8认同)
  • 如果您只需要这个函数(在Android等某些平台上,整个Jar都包含在最终应用程序中),那么通过包含几十KB的额外JAR文件来重用库将不会非常有效.有时,当不需要性能时,更短更清晰的代码会更好. (8认同)
  • @ personne3000可能,但在这种情况下,您需要流支持,而不是单个呼叫功能.这一个很容易理解和记忆,因此需要维护. (2认同)

Ste*_*202 53

番石榴解决方案,为了完整性:

import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);
Run Code Online (Sandbox Code Playgroud)

现在hex"48656c6c6f20776f726c64".

  • 从 Guava 22.0 开始,它是 `HashCode.fromBytes(checksum).toString()` (2认同)

Sal*_*ack 42

Java 17 最终包含HexFormat类,因此您可以简单地执行以下操作:

HexFormat.of().formatHex(bytes);
Run Code Online (Sandbox Code Playgroud)

它支持小写/大写、分隔符、前缀、后缀等配置。

  • 最后,一些不需要外部库或者是一个损坏的解决方案 (8认同)

eve*_*Guy 40

这个简单的oneliner适用于我
String result = new BigInteger(1, inputBytes).toString(16);
编辑 - 使用它将删除前导零,但嘿工作我的用例.感谢@Voicu指出它

  • 这个_oneliner_会丢弃前导零字节. (53认同)

Cor*_*ral 25

使用DataTypeConverter类javax.xml.bind.DataTypeConverter

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

  • Java 11 中删除了类。请参阅:[*JEP 320:删除 Java EE 和 CORBA 模块*](https://openjdk.java.net/jeps/320) (3认同)

Usa*_*oto 18

我会使用这样的东西来固定长度,比如哈希:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你,这是如此简洁和恰当。 (2认同)

Mic*_*erg 17

我在这里找到了三种不同的方式:http: //www.rgagnon.com/javadetails/java-0596.html

正如他所指出的那样,最优雅的一个,我想是这个:

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();
}
Run Code Online (Sandbox Code Playgroud)


hig*_*nse 15

以存储查找表的次要成本,这种实现简单且非常快.

 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);
  }
Run Code Online (Sandbox Code Playgroud)

  • 为什么不用简单的`for`循环初始化`BYTE2HEX`数组? (6认同)

pat*_*ckf 12

以下是一些常见的选项,从简单(单行)到复杂(大型库)。如果您对性能感兴趣,请参见下面的微型基准。

选项1:代码段-简单

一种非常简单的解决方案是使用BigInteger的十六进制表示形式:

new BigInteger(1, someByteArray).toString(16)
Run Code Online (Sandbox Code Playgroud)

请注意,由于此方法处理数字不是任意的字节字符串,因此它将省略前导零-这可能是或可能不是您想要的(例如000AE30AE3对于3字节输入而言)。这也非常慢,与下一个选项相比要慢大约100倍

选项2:代码段-高级

这是一个全功能的,可复制和可粘贴的代码段,支持大写/小写字节序。它经过了优化,可最大程度地减少内存复杂性并提高性能,并且应与所有现代Java版本(5+)兼容。

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;

        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}
Run Code Online (Sandbox Code Playgroud)

可在此处找到具有Apache v2许可证和解码器的完整源代码。

选项3:使用小型优化库:bytes-java

在处理上一个项目时,我创建了这个小工具包,用于处理Java中的字节。它没有外部依赖性,并且与Java 7+兼容。除其他外,它包括非常快速且经过良好测试的HEX编码器/解码器:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()
Run Code Online (Sandbox Code Playgroud)

您可以在Github上检查它:bytes-java

选项4:Apache Commons编解码器

当然,有很好的通用公共编解码器。(前面有警告意见)在进行上面概述的项目时,我分析了代码,并感到非常失望。许多重复的无组织代码,过时的和外来的编解码器可能仅对很少且过度设计和缓慢实现的流行编解码器(特别是Base64)有用。因此,如果您要使用它或其他方法,我将做出明智的决定。无论如何,如果您仍然想使用它,请参见以下代码片段:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));
Run Code Online (Sandbox Code Playgroud)

选项5:Google Guava

通常,您已经将番石榴作为依赖项。如果是这样,请使用:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);
Run Code Online (Sandbox Code Playgroud)

选项6:春季安全性

如果将Spring框架Spring Security一起使用,则可以使用以下内容:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));
Run Code Online (Sandbox Code Playgroud)

选项7:充气城堡

如果您已经使用了安全性框架Bouncy Castle,则可以使用其Hexutil:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);
Run Code Online (Sandbox Code Playgroud)

并非完全是选项8:Java 9+兼容性或“请勿使用JAXB javax / xml / bind / DatatypeConverter”

在以前的Java(8及以下)版本中,JAXB的Java代码作为运行时依赖项包含在内。由于Java 9和Jigsaw模块化,如果没有显式声明,则您的代码无法访问其模块之外的其他代码。因此,请注意是否出现类似以下的异常:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
Run Code Online (Sandbox Code Playgroud)

在具有Java 9+的JVM上运行时。如果是这样,则将实现切换到以上任何一种选择。另请参阅此问题


微观基准

这是一个简单的JMH微基准测试,其编码大小不同的字节数组的结果。这些值是每秒的操作数,因此越高越好。 请注意,微基准测试通常不代表真实世界的行为,因此请一salt而就。

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,5016,66 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |
Run Code Online (Sandbox Code Playgroud)

规格:JDK 8u202,i7-7700K,Win10、24GB Ram。请在此处查看完整的基准。

  • 相当彻底!由于您使用的是 Java 17,因此您可能需要将“HexFormat.of”添加到使用“JDK17+”的用户的选项中。 (3认同)

Eng*_*uad 11

Java 17中添加了HexFormat

String hex = HexFormat.of().formatHex(array);
Run Code Online (Sandbox Code Playgroud)


Man*_*shi 8

这个怎么样?

    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;
    }
Run Code Online (Sandbox Code Playgroud)


Mar*_*ian 8

我们不需要使用任何外部库或基于循环和常量编写代码。
仅此而已就足够了:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Run Code Online (Sandbox Code Playgroud)

  • 这与everconfusedGuy 的答案**非常**相似。 (2认同)

Ron*_*eod 6

这是使用 Streams 的另一种方法:

private static String toHexString(byte[] bytes) {
    return IntStream.range(0, bytes.length)
    .mapToObj(i -> String.format("%02X", bytes[i]))
    .collect(Collectors.joining());
}
Run Code Online (Sandbox Code Playgroud)