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

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

942885 次浏览

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

Apache Commons编解码器库有一个十六进制类来完成这种类型的工作。

import org.apache.commons.codec.binary.Hex;
String foo = "I am a string";byte[] bytes = foo.getBytes();System.out.println( Hex.encodeHexString( bytes ) );

从讨论这里,特别是这个的答案中,这是我目前使用的函数:

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字节1000万次)表明它比任何其他替代方案都快得多,大约是长数组时间的一半。与我从中得到的答案相比,切换到按位操作——正如讨论中建议的那样——为长数组节省了大约20%的时间。(编辑:当我说它比替代方案快时,我的意思是讨论中提供的替代代码。性能相当于Commons Codec,它使用非常相似的代码。)

2k20版本,关于Java9个紧凑字符串:

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

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

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

这个简单的oneliner为我工作
String result = new BigInteger(1, inputBytes).toString(16);
编辑-使用这个将删除前导零,但是他们对我的用例有效。谢谢@Voicu指出它

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

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

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

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

方法#0XML绑定的Java架构(JAXB)的一部分,是将byte[]转换为十六进制字符串的便捷方法。DatatypeConverter类还包括许多其他有用的数据操作方法。

在Java8和更早的时候,JAXB是Java标准库的一部分。它是不建议使用,Java9和删除,Java11,作为将所有JavaEE包移动到它们自己的库中的努力的一部分。说来话长了。现在,javax.xml.bind不存在,如果您想使用包含DatatypeConverter的JAXB,您需要从Maven安装jaxabapiJAXB运行时

示例用法:

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

将导致:

000086003D

这个答案与这一个相同。

我通常使用下面的方法来处理dedef语句,但我不知道这是否是最好的方法

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

我更喜欢用这个:

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

这是对已接受答案的稍微灵活的适应。就我个人而言,我保留了接受的答案和这个重载,在更多的上下文中可用。

@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

这个怎么样?

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

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

class ByteArray:
@classmethoddef char(cls, args=[]):cls.hexArray = "0123456789ABCDEF".encode('utf-16')j = 0length = (cls.hexArray)
if j < length:v = j & 0xFFhexChars = [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=[])

一个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"

使用DataTypeConver类javax.xml.bind.DataTypeConverter

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

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

org.apache.commons.codec.binary.Hex

也许你确实有…

org.apache.xerces.impl.dv.util.赫克斯宾

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

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
  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;}

这是一个类似于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 existsbyte[] 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;}}
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();}

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

  1. 使用循环
  2. 使用javax.xml.bind.DatatypeConver,它编译得很好,但经常在运行时抛出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 {// 00out.println(encode(new byte[] {}));
// 01out.println(encode(new byte[] {1}));
//203040out.println(encode(new byte[] {0x20, 0x30, 0x40}));
// 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732eout.println(encode("All your base are belong to us.".getBytes()));}}

我无法在62个操作码下得到这个,但是如果你可以在第一个字节小于0x10的情况下不使用0填充,那么以下解决方案只使用23个操作码。真正展示了如果本机实现还不可用(或者在这种情况下,如果BigInteger可以选择在toString中使用零作为前缀),“自己容易实现”的解决方案是如何变得非常昂贵的。

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

我的解决方案基于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);}

如果您使用的是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));
Converts bytes data to hex characters
@param bytes byte array to be converted to hex string@return byte String in hex format
private static String bytesToHex(byte[] bytes) {char[] hexChars = new char[bytes.length * 2];int v;for (int j = 0; j < bytes.length; j++) {v = bytes[j] & 0xFF;hexChars[j * 2] = HEX_ARRAY[v >>> 4];hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];}return new String(hexChars);}

为简单的函数添加一个实用程序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));}

}

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

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

选项1a:大整数

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

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

请注意,由于这处理数字而不是任意的字节串,它将省略前导零-这可能是您想要的,也可能不是您想要的(例如,3字节输入的000AE3 vs0AE3)。这也非常慢,与选项2相比大约慢100倍

选项1b:String.format()

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

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

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

如果你仅具有4字节数组,你可以使用整数类的toHexString方法:

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

8字节数组Long也是如此

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

选项1d:JDK17+HexFormat

最后,JDK 17提供了HexFormat的直接十六进制编码的第一级支持:

HexFormat hex = HexFormat.of();hex.formatHex(someByteArray)

选项2:代码片段-高级

这是一个支持大写/小写endian ness的全功能、可复制和可粘贴的代码片段。它经过优化,可最大限度地降低内存复杂性并最大限度地提高性能,应与所有现代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 lengthfinal char[] buffer = new char[byteArray.length * 2];
// choose lower or uppercase lookup tablefinal 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 firstindex = (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);}

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

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

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

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

您可以在Github: bytes-java相关链接上查看。

选项4:Apache Commons编解码器

当然还有好的'ol公共编解码器。(提前警告意见在进行上面概述的项目时,我分析了代码并感到非常失望;大量重复的无组织代码,过时和异国情调的编解码器可能只对极少数并且非常过度设计和缓慢实现的流行编解码器(特别是Base64)有用。因此,如果你想使用它或替代方案,我会做出明智的决定。无论如何,如果你还想使用它,这里有一个代码片段:

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

选项5:Google Guava

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

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

选项6:Spring Security

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

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

选项7:充气城堡

如果您已经使用了安全框架充气城堡,则可以使用其Hex util:

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

选项8:Java9+兼容性或“不使用JAXB javax/xml/bind/DatatypeConverter”

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

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

在Java9+的JVM上运行时。如果是这样,请将实现切换到上述任何替代方案。另请参阅此问题


微观基准

这是一个简单的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 RAM。请参阅完整的基准测试这里

2022年指标更新

以下是当前JMH 1.35,Java17和更高端计算机的结果

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB ||----------------------|-----------:|-----------:|----------:|--------:|| Opt1: BigInteger     |  2,941,403 |  1,389,448 |   242,096 |       5 || Opt2/3: Bytes Lib    | 31,724,981 | 22,786,906 | 6,197,028 |     930 |

规格:JDK temurin 17.0.4、Ryzen 5900X、Win11、24GB DDR4 RAM

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

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

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

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

要使用DatatypeConver:

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

Java17最终包含HexFormat类,因此您可以简单地执行:

HexFormat.of().formatHex(bytes);

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

最近我不得不实现一个十六进制转换器来将字节流以十六进制格式转储到日志中。最初我使用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 R0020   45 2d 45 4e 54 45 52 20 4c 41 53 54 20 54 52 41   E-ENTER LAST TRA0030   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();}

十六进制格式是在Java17中添加的:

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

只需遍历所有字节,使用Integer.toString将它们转换为十六进制字符串(不幸的是,没有带有基数参数的Byte.toString)并将它们全部附加到StringBuilder

byte[] arr;//set it to your valueStringBuilder sb=new StringBuilder(arr.length*2);//1 byte...2 hex digitsfor(int i=0;i<arr.length;i++){sb.append(Integer.toString(arr[i],16));}String hexValue=sb.toString();

这类似于指针为空中的这个答案,但它使用Integer.toString而不是String.format来提高性能。

如果要使其更具可读性并将字节彼此分开,可以在Java17+中使用以下代码:

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