我有一个字节数组充满十六进制数字和打印它的简单方式是相当没有意义的,因为有许多不可打印的元素。我需要的是精确的十六进制形式:3a5f771c


当前回答

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

其他回答

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

如果你想让它更具可读性,并将字节彼此分开,你可以在Java 17+中使用以下代码:

byte[] yourByteArray = { -128, 0, 127 };
String hexString = new String(HexFormat.ofDelimiter(" ").formatHex(yourByteArray));
// 80 00 7f

为了完整起见,一个番石榴解决方案:

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

现在十六进制是“48656c6c6f20776f726c64”。

如果您正在为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=[])

最近我必须实现一个十六进制转换器,以十六进制格式将字节流转储到日志中。最初我是用海克斯做的。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();
}