Java代码将字节转换为hex

我有一个字节数组。 我希望该数组的每个字节的string被转换为其对应的hex值。

在Java中有任何function将字节数组转换为hex?

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语法
    • %[flags][width]conversion
      • 标志'0' – 结果将被填零
      • 宽度2
      • 转换'X' – 结果被格式化为hex整数,大写

看问题的文本,这也是可能的,这是要求:

  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位byte被符号扩展为32位int 。 为了有效地撤销这个符号扩展,可以用0xFF来屏蔽这个byte

  byte b = -1; System.out.println(Integer.toHexString(b & 0xFF)); // prints "ff" 

使用toHexString另一个问题是它不toHexString零:

  byte b = 10; System.out.println(Integer.toHexString(b & 0xFF)); // prints "a" 

这两个因素结合起来应该使String.format解决scheme更可取。

参考

  • JLS 4.2.1积分types和值
    • 对于byte ,从-128127 (含)
  • JLS 5.1.2扩展原始转换

我发布,因为没有任何现有的答案解释了为什么他们的方法工作,我认为这是非常重要的这个问题。 在某些情况下,这会导致提出的解决scheme显得不必要的复杂和微妙。 为了说明,我将提供一个相当直接的方法,但是我会提供更多的细节来帮助说明它为什么起作用。

首先,我们要做什么? 我们希望将一个字节值(或一个字节数组)转换为一个代表ASCII码的hex值的string。 所以第一步是找出Java中的一个字节是什么:

字节数据types是一个8位有符号的二进制补码整数 。 最小值为-128,最大值为127(含)。 字节数据types对于在大型数组中保存内存很有用,其中内存节省实际上很重要。 在限制有助于澄清你的代码的地方,它们也可以用来代替int; variables范围有限的事实可以作为一种文档forms。

这是什么意思? 一些事情:首先也是最重要的,这意味着我们正在使用8位 。 例如,我们可以将数字2写为0000 0010.但是,由于它是二进制补码,所以我们写了一个负数2,如下所示:1111 1110.也就是说,转换为hex非常简单。 也就是说,您只需将每个4位段直接转换为hex。 请注意,要理解此scheme中的负数,您首先需要了解二进制补码。 如果你还不明白补码,你可以阅读一个很好的解释,在这里: http : //www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html


将二进制补码转换为hex

一旦一个数字在二进制补码中,将其转换为hex是很简单的。 一般来说,从二进制转换为hex是非常简单的,正如你将在下面两个例子中看到的,你可以直接从二进制补码到hex。

例子

示例1:将2转换为hex。

1)首先将2转换成二进制补码:

 2 (base 10) = 0000 0010 (base 2) 

2)现在将二进制转换为hex:

 0000 = 0x0 in hex 0010 = 0x2 in hex therefore 2 = 0000 0010 = 0x02. 

示例2:将-2(以二进制补码forms)转换为hex。

1)首先将-2换成二进制补码:

 -2 (base 10) = 0000 0010 (direct conversion to binary) 1111 1101 (invert bits) 1111 1110 (add 1) therefore: -2 = 1111 1110 (in two's complement) 

2)现在转换为hex:

 1111 = 0xF in hex 1110 = 0xE in hex therefore: -2 = 1111 1110 = 0xFE. 

在Java中这样做

现在我们已经介绍了这个概念,你会发现我们可以通过一些简单的掩饰和移位来实现我们想要的。 要理解的关键是你正试图转换的字节已经是二进制补码了。 你不要自己做这个转换。 我认为这是在这个问题上混淆的主要观点。 以下面的字节数组为例:

 byte[] bytes = new byte[]{-2,2}; 

我们只是手动将它们转换为hex,但是我们怎样才能在Java中完成呢? 就是这样:

第1步:创build一个StringBuffer来保存我们的计算。

 StringBuffer buffer = new StringBuffer(); 

步骤2:隔离高阶位,将它们转换为hex,并将它们追加到缓冲区

给定二进制数1111 1110,我们可以通过首先将它们移位4来隔离高位,然后将剩下的数字清零。 从逻辑上讲,这很简单,但是,Java(以及许多语言)中的实现细节由于符号扩展而引入了一个皱折。 从本质上讲,当你转换一个字节值时,Java首先将你的值转换成一个整数,然后执行符号扩展。 所以,虽然你会预期1111 1110 >> 4为0000 1111,但实际上,在Java中它被表示为2的补码0xFFFFFFFF!

所以回到我们的例子:

 1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two's complement) 

然后,我们可以用掩码来隔离这些位:

 1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 therefore: 1111 = 0xF in hex. 

在Java中,我们可以做到这一切:

 Character.forDigit((bytes[0] >> 4) & 0xF, 16); 

forDigit函数只是将您传递的数字映射到一组hex数字0-F。

第3步:接下来我们需要隔离低位。 由于我们想要的位已经处于正确的位置,我们可以将它们掩盖起来:

 1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before) therefore: 1110 = 0xE in hex. 

像以前一样,在Java中,我们可以做到这一切:

 Character.forDigit((bytes[0] & 0xF), 16); 

把这一切放在一起,我们可以做一个for循环,并转换整个数组:

 for(int i=0; i < bytes.length; i++){ buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16)); buffer.append(Character.forDigit((bytes[i] & 0xF), 16)); } 

希望这个解释能让你更清楚地知道你在互联网上find的许多例子究竟是怎么回事。 希望我没有犯任何令人震惊的错误,但build议和更正非常欢迎!

试试这个方法:

 byte bv = 10; String hexString = Integer.toHexString(bv); 

处理数组(如果我正确理解你的话):

 byte[] bytes = {9, 10, 11, 15, 16}; StringBuffer result = new StringBuffer(); for (byte b : bytes) { result.append(String.format("%02X ", b)); result.append(" "); // delimiter } return result.toString(); 

作为polygenelubricants提到, String.format()是正确的答案比Integer.toHexString() (因为它负责处理负数)。

如果您很乐意使用外部库,则org.apache.commons.codec.binary.Hex类具有一个encodeHex方法,它需要一个byte[]并返回一个char[] 。 这种方法比格式选项快得多,并且封装了转换的细节。 另外还有一个用于相反转换的decodeHex方法。

我发现这样做的最快方法如下:

 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倍。 如果你想testing它:

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

编辑 :刚刚find一些更快,并保持在一条线上,但与JRE 9 兼容使用风险自负

 import javax.xml.bind.DatatypeConverter; DatatypeConverter.printHexBinary(raw); 

最好的解决scheme是这个坏蛋一行:

 String hex=DatatypeConverter.printHexBinary(byte[] b); 

如这里所述

如果你想要一个恒定宽度的hex表示,即0A而不是A ,这样你可以明确地恢复字节,尝试format()

 StringBuilder result = new StringBuilder(); for (byte bb : byteArray) { result.append(String.format("%02X", bb)); } return result.toString(); 
 BigInteger n = new BigInteger(byteArray); String hexa = n.toString(16)); 

其他人已经涵盖了一般情况。 但是如果你有一个已知forms的字节数组,例如一个MAC地址,那么你可以:

 byte[] mac = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }; String str = String.format("%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 

这是我发现跑得最快的代码。 我在23ms内将它运行在长度为32的109015个字节数组上。 我在虚拟机上运行它,所以它可能会在裸机上运行得更快。

 public static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; public static char[] encodeHex( final byte[] data ){ final int l = data.length; final char[] out = new char[l<<1]; for( int i=0,j=0; i<l; i++ ){ out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4]; out[j++] = HEX_DIGITS[0x0F & data[i]]; } return out; } 

那么你可以做

 String s = new String( encodeHex(myByteArray) ); 
 org.bouncycastle.util.encoders.Hex.toHexString(byteArray); 

要么

 org.apache.commons.codec.binary.Hex.encodeHexString(byteArray); 

这是一个简单的函数来转换字节为hex

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

我无法弄清楚你是什么意思的字节string,但这里有一些转换从字节到string,反之亦然,当然有更多的官方文件

 Integer intValue = 149; 

相应的字节值是:

 Byte byteValue = intValue.byteValue(); // this will convert the rightmost byte of the intValue to byte, because Byte is an 8 bit object and Integer is at least 16 bit, and it will give you a signed number in this case -107 

从Bytevariables中获取整数值:

 Integer anInt = byteValue.intValue(); // This will convert the byteValue variable to a signed Integer 

从字节和整数到hexstring:
这是我做的方式:

 Integer anInt = 149 Byte aByte = anInt.byteValue(); String hexFromInt = "".format("0x%x", anInt); // This will output 0x95 String hexFromByte = "".format("0x%x", aByte); // This will output 0x95 

将字节数组转换为hexstring:
据我所知,没有简单的函数来将一个Object的数组内的所有元素转换成另一个Object元素,所以你必须自己做。 您可以使用以下function:

从字节[]到string:

  public static String byteArrayToHexString(byte[] byteArray){ String hexString = ""; for(int i = 0; i < byteArray.length; i++){ String thisByte = "".format("%x", byteArray[i]); hexString += thisByte; } return hexString; } 

并从hexstring到字节[]:

 public static byte[] hexStringToByteArray(String hexString){ byte[] bytes = new byte[hexString.length() / 2]; for(int i = 0; i < hexString.length(); i += 2){ String sub = hexString.substring(i, i + 2); Integer intVal = Integer.parseInt(sub, 16); bytes[i / 2] = intVal.byteValue(); String hex = "".format("0x%x", bytes[i / 2]); } return bytes; } 

太晚了,但我希望这可以帮助其他人;)

如果性能是一个问题,创build(和销毁)一堆String实例并不是一个好方法。

请忽略冗长的(重复的)参数检查语句( if是)。 这是(另一个)教育目的。

完整的maven项目: http : //jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/

编码…

 /** * Encodes a single nibble. * * @param decoded the nibble to encode. * * @return the encoded half octet. */ protected static int encodeHalf(final int decoded) { switch (decoded) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: return decoded + 0x30; // 0x30('0') - 0x39('9') case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: return decoded + 0x57; // 0x41('a') - 0x46('f') default: throw new IllegalArgumentException("illegal half: " + decoded); } } /** * Encodes a single octet into two nibbles. * * @param decoded the octet to encode. * @param encoded the array to which each encoded nibbles are written. * @param offset the offset in the array. */ protected static void encodeSingle(final int decoded, final byte[] encoded, final int offset) { if (encoded == null) { throw new IllegalArgumentException("null encoded"); } if (encoded.length < 2) { // not required throw new IllegalArgumentException( "encoded.length(" + encoded.length + ") < 2"); } if (offset < 0) { throw new IllegalArgumentException("offset(" + offset + ") < 0"); } if (offset >= encoded.length - 1) { throw new IllegalArgumentException( "offset(" + offset + ") >= encoded.length(" + encoded.length + ") - 1"); } encoded[offset] = (byte) encodeHalf((decoded >> 4) & 0x0F); encoded[offset + 1] = (byte) encodeHalf(decoded & 0x0F); } /** * Decodes given sequence of octets into a sequence of nibbles. * * @param decoded the octets to encode * * @return the encoded nibbles. */ protected static byte[] encodeMultiple(final byte[] decoded) { if (decoded == null) { throw new IllegalArgumentException("null decoded"); } final byte[] encoded = new byte[decoded.length << 1]; int offset = 0; for (int i = 0; i < decoded.length; i++) { encodeSingle(decoded[i], encoded, offset); offset += 2; } return encoded; } /** * Encodes given sequence of octets into a sequence of nibbles. * * @param decoded the octets to encode. * * @return the encoded nibbles. */ public byte[] encode(final byte[] decoded) { return encodeMultiple(decoded); } 

解码…

 /** * Decodes a single nibble. * * @param encoded the nibble to decode. * * @return the decoded half octet. */ protected static int decodeHalf(final int encoded) { switch (encoded) { case 0x30: // '0' case 0x31: // '1' case 0x32: // '2' case 0x33: // '3' case 0x34: // '4' case 0x35: // '5' case 0x36: // '6' case 0x37: // '7' case 0x38: // '8' case 0x39: // '9' return encoded - 0x30; case 0x41: // 'A' case 0x42: // 'B' case 0x43: // 'C' case 0x44: // 'D' case 0x45: // 'E' case 0x46: // 'F' return encoded - 0x37; case 0x61: // 'a' case 0x62: // 'b' case 0x63: // 'c' case 0x64: // 'd' case 0x65: // 'e' case 0x66: // 'f' return encoded - 0x57; default: throw new IllegalArgumentException("illegal half: " + encoded); } } /** * Decodes two nibbles into a single octet. * * @param encoded the nibble array. * @param offset the offset in the array. * * @return decoded octet. */ protected static int decodeSingle(final byte[] encoded, final int offset) { if (encoded == null) { throw new IllegalArgumentException("null encoded"); } if (encoded.length < 2) { // not required throw new IllegalArgumentException( "encoded.length(" + encoded.length + ") < 2"); } if (offset < 0) { throw new IllegalArgumentException("offset(" + offset + ") < 0"); } if (offset >= encoded.length - 1) { throw new IllegalArgumentException( "offset(" + offset + ") >= encoded.length(" + encoded.length + ") - 1"); } return (decodeHalf(encoded[offset]) << 4) | decodeHalf(encoded[offset + 1]); } /** * Encodes given sequence of nibbles into a sequence of octets. * * @param encoded the nibbles to decode. * * @return the encoded octets. */ protected static byte[] decodeMultiple(final byte[] encoded) { if (encoded == null) { throw new IllegalArgumentException("null encoded"); } if ((encoded.length & 0x01) == 0x01) { throw new IllegalArgumentException( "encoded.length(" + encoded.length + ") is not even"); } final byte[] decoded = new byte[encoded.length >> 1]; int offset = 0; for (int i = 0; i < decoded.length; i++) { decoded[i] = (byte) decodeSingle(encoded, offset); offset += 2; } return decoded; } /** * Decodes given sequence of nibbles into a sequence of octets. * * @param encoded the nibbles to decode. * * @return the decoded octets. */ public byte[] decode(final byte[] encoded) { return decodeMultiple(encoded); } 

这是一个非常快的方法。 没有需要外部的库。

 final protected static char[] HEXARRAY = "0123456789abcdef".toCharArray(); public static String encodeHexString( 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] = HEXARRAY[v >>> 4]; hexChars[j * 2 + 1] = HEXARRAY[v & 0x0F]; } return new String(hexChars); } 

使用

 Integer.toHexString((int)b);