ChatGPT解决这个技术问题 Extra ChatGPT

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

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

为什么不先试一试,然后向我们展示您所拥有的。你没有什么可失去的,也没有什么可得到的。 Integer 有一个 toHexString(...) 方法,如果这是您正在寻找的,它可能会有所帮助。 String.format(...) 还可以使用 %2x 代码字符串做一些巧妙的格式化技巧。
“我需要的是格式为:3a5f771c ...”的确切十六进制代码-您要求提供确切的形式,但您没有提供确切的示例。继续您提供的内容,将前四个字节转换为字符串,然后将省略号连接到字符串。
借助 Java 8 中的流,它可以简单地实现为: static String byteArrayToHex(byte[] a) { return IntStream.range(0, a.length) .mapToObj(i -> String.format("%02x ", a[i])) .reduce((acc, v) -> acc + " " + v) .get(); }
Java 17 救援:HexFormat.of().formatHex(bytes)

E
Evgeniy Berezovsky

从讨论 here,尤其是 this 答案来看,这是我目前使用的功能:

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

我自己的小型基准测试(一千次一百万字节,一千万次 256 字节)表明它比任何其他替代方案都要快得多,大约是长数组的一半时间。与我从中得到的答案相比,切换到按位运算——正如讨论中所建议的——将长数组的时间缩短了 20%。 (编辑:当我说它比替代方案更快时,我的意思是讨论中提供的替代代码。性能相当于 Commons Codec,它使用非常相似的代码。)

2k20 版本,关于 Java 9 紧凑字符串:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[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, StandardCharsets.UTF_8);
}

我刚刚找到 javax.xml.bind.DataTypeConverter,它是标准发行版的一部分。当您搜索此类问题时,为什么不会出现这种情况?许多有用的工具,包括 String printHexBinary(byte[])byte[] parseHexBinary(String)。但是,printHexBinary 比此答案中的函数慢很多 (2x)。 (我检查了源代码;它使用了一个 stringBuilderparseHexBinary 使用了一个数组。)不过,实际上,对于大多数用途来说,它已经足够快了,而且您可能已经拥有它了。
+1 的答案,因为 Android 没有 DataTypeConverter
@maybeWeCouldStealAVan:JDK 7 现在是开源的。我们应该提交一个补丁来提高 printHexBinary 的性能吗?
javax.xml.bind.DataTypeConverter 正在从 Java 11 中删除。
为什么此行 int v = bytes[j] & 0xFF; 中需要 & 0xFF?我错过了什么还是只是没有必要?
M
MultiplyByZer0

Apache Commons Codec 库有一个 Hex 类来完成这种类型的工作。

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

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

@cytinus - 我的反对票发生在 4 个月前,所以我不完全确定我在想什么,但我可能反对图书馆的规模。这是程序中的一个小功能;无需在项目中添加如此庞大的库来执行它。
@ArtOfWarefare 我同意,所以你可以做 import org.apache.commons.codec.binary.Hex; 而不是 import org.apache.commons.codec.*;
@ArtOfWarfare 我不同意。唯一可怕的是,JRE 和 JDK 默认不包含 apache 公共库。有些库非常有用,默认情况下它们确实应该在您的类路径中,这就是其中之一。
我强烈建议将此答案交换为最佳答案。始终投票选择使用经过良好测试的、高性能的开源库,而不是没有改进的自定义代码。
或者,如果您使用 BouncyCastle (org.bouncycastle:bcprov-jdk15on),您可以使用此类:org.bouncycastle.util.encoders.Hex,使用此方法:String toHexString(byte[] data)
M
MultiplyByZer0

方法 javax.xml.bind.DatatypeConverter.printHexBinary()Java Architecture for XML Binding (JAXB) 的一部分,是一种将 byte[] 转换为十六进制字符串的便捷方法。 DatatypeConverter 类还包括许多其他有用的数据操作方法。

在 Java 8 及更早版本中,JAXB 是 Java 标准库的一部分。 deprecated 使用 Java 9,removed 使用 Java 11,这是将所有 Java EE 包移到自己的库中的努力的一部分。 It's a long story。现在,javax.xml.bind 不存在,如果您想使用包含 DatatypeConverter 的 JAXB,您需要从 Maven 安装 JAXB APIJAXB Runtime

示例用法:

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

将导致:

000086003D

此答案与 this one 相同。


一个很好的解决方案,但遗憾的是它在 Android 中不是有效的。
@Kazriko 也许您想阅读 code.google.com/p/dalvik/wiki/JavaxPackages。这是一种将 javax 类导入 Android 的方法。但是,如果您只想转换为十六进制,那就不值得了。
自 JDK 9 起,DatatypeConverter 不再可访问
@PhoneixS 它仍然存在,但不是默认运行时的一部分(由于 Java 9 模块)。
不要依赖 javax.xml.bind,它编译得很好,但在运行时找不到。如果这样做,请准备好处理 java.lang.NoClassDefFoundError
P
Pointer Null

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

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

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

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

选项 1:代码片段 - 简单(仅使用 JDK/Android)

选项 1a:大整数

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

new BigInteger(1, someByteArray).toString(16);

请注意,由于这处理 numbers 而不是任意 byte-strings 它将省略前导零 - 这可能是也可能不是您想要的(例如 000AE3 vs 0AE3 for一个 3 字节的输入)。这也很慢,与选项 2 相比,大约慢了 100 倍

选项 1b:String.format()

使用 %X 占位符,String.format() 能够将大多数原始类型(shortintlong)编码为十六进制:

String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());

选项 1c:整数/长整数(仅 4/8 字节数组)

如果您只有 4 字节数组,您可以使用 Integer 类的 toHexString 方法:

Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());

8 字节数组Long 也是如此

Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());

选项 2:代码片段 - 高级

这是一个功能齐全的副本和支持大写/小写endianness的可粘贴代码段。它经过优化以最小化内存复杂性和最大化性能,并且应该与所有现代 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);
}

可在 here 中找到带有 Apache v2 许可和解码器的完整源代码。

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

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

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

您可以在 Github: bytes-java 上查看。

选项 4:Apache Commons 编解码器

当然有好的'ol commons codecs。 (提前警告在处理上述项目时,我分析了代码,但非常失望;大量重复的无组织代码、过时和异国情调的编解码器可能仅适用于流行编解码器(特别是 Base64)的极少数且设计过度且缓慢的实现。因此,如果您想使用它或替代方案,我会做出明智的决定。 无论如何,如果您仍想使用它,这里有一个代码片段:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

选项 5:谷歌番石榴

通常情况下,您已经将 Guava 作为依赖项。如果是这样,只需使用:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

选项 6:Spring 安全性

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

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

选项 7:充气城堡

如果您已使用安全框架 Bouncy Castle,则可以使用其 Hex 实用程序:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

不是真正的选项 8:Java 9+ 兼容性或“不要使用 JAXBs javax/xml/bind/DatatypeConverter”

在以前的 Java(8 及以下)版本中,JAXB 的 Java 代码作为运行时依赖项包含在内。由于 Java 9 和 Jigsaw modularisation 您的代码在没有明确声明的情况下无法访问其模块之外的其他代码。因此请注意,如果您遇到以下异常:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

在使用 Java 9+ 的 JVM 上运行时。如果是这样,则将实现切换到上述任何替代方案。另请参阅此question

微观基准

以下是对不同大小的字节数组进行简单编码的JMH微基准测试的结果。这些值是每秒操作数,因此越高越好。请注意,微基准测试通常并不代表真实世界的行为,因此请对这些结果持保留态度。

| 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,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

规格:JDK 8u202、i7-7700K、Win10、24GB 内存。查看完整的基准 here


S
Stephan202

为了完整起见,Guava 解决方案:

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

现在 hex"48656c6c6f20776f726c64"


在 Guava 中,您还可以使用 new HashCode(bytes).toString()
从 Guava 22.0 开始,它是 HashCode.fromBytes(checksum).toString()
e
everconfusedGuy

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


这个 oneliner 丢弃前导零字节。
@Voicu ...它会在 50% 的时间内添加前导零。
U
Usagi Miyamoto

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

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

谢谢你,这很简洁和恰当。
S
Soumyaansh

使用 DataTypeConverter 类javax.xml.bind.DataTypeConverter

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


Java 11 中删除了类。请参阅:JEP 320: Remove the Java EE and CORBA Modules
M
Michael Bisbjerg

我在这里找到了三种不同的方法: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();
}

其他方法在 5 毫秒内运行在我的 64 字节样本上,这个在 0 毫秒内运行。可能最适合缺少任何其他字符串函数,如格式。
if (raw == null) return null 不会很快失败。为什么你会使用 null 键?
我想输入验证是一种习惯。在这种情况下,我们会阻止任何 Null 引用异常,并将其留给调用者来处理不良数据。
h
higginse

以存储查找表的少量成本,此实现简单且非常快速。

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

为什么不用简单的 for 循环初始化 BYTE2HEX 数组?
@icza 是否有可能使用静态最终(又名常量)字段?
@nevelis 它可以在 static { } 块中分配。
@icza 因为硬编码查找表比生成它更快。这里内存复杂度与时间复杂度进行交易,即。需要更多内存但速度更快(两端稍微有点)
S
Saljack

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

HexFormat.of().formatHex(bytes);

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


最后,不需要外部库或者是一个损坏的解决方案
M
Marian

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

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);

这与everconfusedGuy's Answer 非常相似。
J
JoseM

这个怎么样?

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

E
Eng.Fouad

Java 17 中添加了 HexFormat

String hex = HexFormat.of().formatHex(array);

R
Ron McLeod

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

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

O
Oleksandr Potomkin
public static String toHexString(byte[] bytes) {

    StringBuilder sb = new StringBuilder();

    if (bytes != null) 
        for (byte b:bytes) {

            final String hexString = Integer.toHexString(b & 0xff);

            if(hexString.length()==1)
                sb.append('0');

            sb.append(hexString);//.append(' ');
        }

      return sb.toString();//.toUpperCase();
}

要使用 DatatypeConverter:

public String toHexString(byte... bytes) {

    return Optional.ofNullable(bytes)
            .filter(bs->bs.length>0)
            .map(DatatypeConverter::printHexBinary)
            .map(str->IntStream.range(0, str.length())
                    .filter(i->(i%2)==0)        // take every second index
                    .mapToObj(i->"0x" + str.substring(i, i+2))
                    .collect(Collectors.joining(" ")))
            .orElse("");
}

S
Suraj Rao

为简单功能添加实用程序 jar 并不是一个好的选择。而是组装您自己的实用程序类。以下是可能更快的实施。

public class ByteHex {

    public static int hexToByte(char ch) {
        if ('0' <= ch && ch <= '9') return ch - '0';
        if ('A' <= ch && ch <= 'F') return ch - 'A' + 10;
        if ('a' <= ch && ch <= 'f') return ch - 'a' + 10;
        return -1;
    }

    private static final String[] byteToHexTable = new String[]
    {
        "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
        "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
        "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
        "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
        "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
        "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
        "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
        "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
        "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
        "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
        "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
        "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
        "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
    };

    private static final String[] byteToHexTableLowerCase = new String[]
    {
        "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
        "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
        "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
        "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
        "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
        "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
        "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
        "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
        "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
        "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
        "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
        "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
        "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
    };

    public static String byteToHex(byte b){
        return byteToHexTable[b & 0xFF];
    }

    public static String byteToHex(byte[] bytes){
        if(bytes == null) return null;
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
        return sb.toString();
    }

    public static String byteToHex(short[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
        return sb.toString();
    }

    public static String byteToHexLowerCase(byte[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
        return sb.toString();
    }

    public static byte[] hexToByte(String hexString) {
        if(hexString == null) return null;
        byte[] byteArray = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
        }
        return byteArray;
    }

    public static byte hexPairToByte(char ch1, char ch2) {
        return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
    }


}

j
java-addict301

如果您使用的是 Spring Security 框架,则可以使用:

import org.springframework.security.crypto.codec.Hex

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

B
Bamaco

我更喜欢使用这个:

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

它是对已接受答案的稍微灵活的调整。就个人而言,我同时保留了已接受的答案和此超载,可在更多情况下使用。


最初的问题是 byte[] 到 String。查看字节[] 的十六进制或问一个不同的问题,@NonExistent。
l
lucky1928

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

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

如果您的 debuffer 遇到了糟糕的一天,请尝试在 StringBuilder 实例化中添加一些要支持的字符:StringBuilder buf = new StringBuilder(data.length * 2);
A
Aaron Cooley

好的,所以有很多方法可以做到这一点,但是如果你决定使用一个库,我建议在你的项目中查看是否在添加新库之前已经在你的项目中的库中实现了某些东西只是为了做到这一点。例如,如果您还没有

org.apache.commons.codec.binary.Hex

也许你确实有...

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


S
SajithP

最近我不得不实现一个十六进制转换器来将字节流转储到十六进制格式的日志中。最初我使用这里已经讨论过的 Hex.encodeHex 来完成它。

但是,如果您想以非常美观/可读的方式表示字节数组,io.netty.buffer 库可能是一个很好的用途,因为它可以打印出十六进制以及其中的字符串,从而消除了不可打印的字符。

要求是这样的,

0010   56 56 09 35 32 f0 b2 00 50 4c 45 41 53 45 20 52   VV.52...PLEASE R
0020   45 2d 45 4e 54 45 52 20 4c 41 53 54 20 54 52 41   E-ENTER LAST TRA
0030   4e 53 41 43 54 49 4f 4e 00 04                     NSACTION..

使用 io.netty.buffer 以更美观的方式执行相同操作的最短方法是

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;

void hexDump(byte[] buf) {
    ByteBuf byteBuf = Unpooled.wrappedBuffer(buf);
    log.trace("Bytes received (Hex)\n" + ByteBufUtil.prettyHexDump(byteBuf.slice()));
}

如果您使用的是 maven,请在 pom.xml 中包含以下依赖项(在 netty 页面中检查最新版本)

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-buffer</artifactId>
    <version>4.1.68.Final</version>
</dependency>

输出是:

         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000010| 40 40 b3 f3 80 f3 80 f3 80 f1 48 f1 41 f1 4e f1 |@@........H.A.N.|
|00000020| 47 f1 49 f1 4e f1 47 b5 f1 52 f1 4f f1 43 f1 4b |G.I.N.G..R.O.C.K|
|00000030| f3 80 f3 80 41 b4 40 40 f3 80 f3 80 40 f3 80 04 |....A.@@....@...|
+--------+-------------------------------------------------+----------------+

供您参考,使用答案中讨论的方法的漫长方法(可能不是最有效的)是,

public static String hexDump(byte[] buf) throws DecoderException
{
    ByteBuffer byteBuf = ByteBuffer.wrap(buf);
    char[] result = Hex.encodeHex(byteBuf);

    String bin = new String(result).toUpperCase();
    String str = new String(Hex.decodeHex(bin), StandardCharsets.UTF_8);

    str = str.replaceAll("[^!-~]", ".");
    StringBuilder out = new StringBuilder();
    int bytes_per_line = 16;

    for (int pos = 0; pos < str.length(); pos += bytes_per_line) {
        out.append(String.format("%04X   ", pos));
        if (2 * (pos + bytes_per_line) >= bin.length()) {
            out.append(String.format("%-" + 2 * bytes_per_line + "s", bin.substring(2 * pos)).replaceAll("..", "$0 "));
        } else {
            out.append(bin.substring(2 * pos, 2 * (pos + bytes_per_line)).replaceAll("..", "$0 "));
        }
        out.append("   ");
        if (pos + bytes_per_line > str.length()) {
            out.append(str.substring(pos));

        } else {
            out.append(str.substring(pos, pos + bytes_per_line));
        }
        out.append("\n");
    }

    return out.toString();
}

C
Campa

@maybewecouldstealavan 提出的解决方案的一个小变体,它可以让您在输出十六进制字符串中直观地将 N 个字节捆绑在一起:

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

那是:

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

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

D
Dmitry

在此页面上找不到任何解决方案

使用循环使用 javax.xml.bind.DatatypeConverter 编译良好但经常在运行时抛出 java.lang.NoClassDefFoundError 。

这是一个没有上述缺陷的解决方案(虽然我的承诺没有其他缺陷)

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 可以选择在字符串)。

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

N
Netherwire

我的解决方案基于 MaybeWeCouldStealAVan 的解决方案,但不依赖于任何额外分配的查找表。它不使用任何“int-to-char”转换技巧(实际上,Character.forDigit() 会这样做,执行一些比较以检查数字的真实含义),因此可能会慢一些。请随时在任何您想要的地方使用它。干杯。

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

f
fuweichin

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

import java.util.Arrays;

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_CASE=new Encoder(false);
        static final Encoder UPPER_CASE=new Encoder(true);
    }
    public static Encoder getEncoder(){
        return Encoder.LOWER_CASE;
    }
    public static Encoder getUpperEncoder(){
        return Encoder.UPPER_CASE;
    }

    public static class Decoder{
      private static int maxIndex=102;
      private static int[] toIndex;
      static {
        toIndex=new int[maxIndex+1];
        Arrays.fill(toIndex, -1);
        char[] chars={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f'};
        for(int i=0; i<chars.length; i++) {
          toIndex[(int)chars[i]]=i;
        }
      }
      public Decoder() {
      }
      public byte[] decode(String str) {
          char[] value=str.toCharArray();
          int start=0;
          if(value.length>2 && value[0]=='0' && (value[1]=='x' || value[1]=='X')) {
            start=2;
          }
          int byteLength=(value.length-start)/2; // ignore trailing odd char if exists
          byte[] data=new byte[byteLength];
          for(int i=start,j=0;i<value.length;i+=2,j++){
              int i1;
              int i2;
              char c1=value[i];
              char c2=value[i+1];
              if(c1>maxIndex || (i1=toIndex[(int)c1])<0 || c2>maxIndex || (i2=toIndex[(int)c2])<0) {
                throw new IllegalArgumentException("Invalid character at "+i);
              }
              data[j]=(byte)((i1<<4)+i2);
          }
          return data;
      }
      static final Decoder IGNORE_CASE=new Decoder();
  }
  public static Decoder getDecoder(){
      return Decoder.IGNORE_CASE;
  }
}

h
helencrump

如果您正在为 python 寻找一个与此完全相同的字节数组,我已将此 Java 实现转换为 python。

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=[])

田咖啡
  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;
  } 

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