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

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


当前回答

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

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

其他回答

    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扩大原语转换

BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16);

如果你使用奇妙仙子,那么有:

package com.google.crypto.tink.subtle;

public final class Hex {
  public static String encode(final byte[] bytes) { ... }
  public static byte[] decode(String hex) { ... }
}

所以像这样的东西应该是有用的:

import com.google.crypto.tink.subtle.Hex;

byte[] bytes = {-1, 0, 1, 2, 3 };
String enc = Hex.encode(bytes);
byte[] dec = Hex.decode(enc)

使用BigInteger将byte[]转换为十六进制字符串的简单方法:

import java.math.BigInteger;

byte[] bytes = new byte[] {(byte)255, 10, 20, 30};
String hex = new BigInteger(1, bytes).toString(16);
System.out.println(hex); // ff0a141e

它是如何工作的?

内置的系统类java.math.BigInteger类(java.math.BigInteger)兼容二进制和十六进制数据:

它有一个构造函数BigInteger(signum=1, byte[])通过byte[]创建一个大整数(设置它的第一个参数signum=1以正确处理负字节) 使用BigInteger.toString(16)将大整数转换为十六进制字符串 要解析十六进制数,请使用new BigInteger("ffa74b", 16) -不能正确处理前导零

如果你想在十六进制结果中有前导零,检查它的大小,并在必要时添加缺少的零:

if (hex.length() % 2 == 1)
    hex = "0" + hex;

笔记

使用new BigInteger(1, bytes),而不是new BigInteger(bytes),因为Java“被设计破坏了”,字节数据类型不包含字节,而是有符号的小整数[-128…127]。如果第一个字节是负的,BigInteger假设您传递了一个负的大整数。只需传递1作为第一个参数(signum=1)。

从十六进制转换回字节[]是棘手的:有时前导零进入产生的输出,它应该像这样被清除:

byte[] bytes = new BigInteger("ffa74b", 16).toByteArray();
if (bytes[0] == 0) {
    byte[] newBytes = new byte[bytes.length - 1];
    System.arraycopy(bytes, 1, newBytes, 0, newBytes.length);
    bytes = newBytes;
}

最后一个提示是如果字节[]有几个前导零,它们将丢失。

我所发现的最快的方法是:

private static final String    HEXES    = "0123456789ABCDEF";

static String getHex(byte[] raw) {
    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();
}

它比String.format快50倍。如果你想测试它:

public class MyTest{
    private static final String    HEXES        = "0123456789ABCDEF";

    @Test
    public void test_get_hex() {
        byte[] raw = {
            (byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
            (byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
            (byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
            (byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
            (byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
            (byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
            (byte) 0xd6, (byte) 0x10,
        };

        int N = 77777;
        long t;

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                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)));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 50
        }

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                StringBuilder hex = new StringBuilder(2 * raw.length);
                for (byte b : raw) {
                    hex.append(String.format("%02X", b));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 2535
        }

    }
}

编辑:刚刚发现一些东西只是稍微快一点,保持在一行,但与JRE 9不兼容。使用风险自负

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);