How to convert a byte array to a hex string in Java?
我用十六进制数字填充了一个字节数组,并且以简单的方式打印它是毫无意义的,因为有许多不可打印的元素。我需要的是:
从这里的讨论,特别是这个答案,这是我目前使用的功能:
1 2 3 4 5 6 7 8 9 10 | private final static char[] hexArray ="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] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } |
我自己的小基准测试(一百万字节1000倍,256字节1000万倍)显示它比任何其他替代方法都快,大约是长数组的一半。与我从中得到的答案相比,切换到位操作(如讨论中所建议的)可以为长数组节省大约20%的时间。(编辑:当我说它比备选方案更快时,我是指讨论中提供的备选方案代码。性能相当于通用编解码器,它使用非常相似的代码。)
ApacheCommons编解码器库有一个十六进制类,用于执行这种类型的工作。
1 2 3 4 5 |
使用
例如:
1 2 | byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61}; System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(bytes)); |
将导致:
1 | 000086003D |
如您所见,这将检索十六进制字符串,该字符串表示带前导零的字节数组。
这个答案与Java中的问题基本相同,如何在保持前导零的同时,将字节数组转换为十六进制数字串?
最简单的解决方案,无外部libs,无数字常量:
1 2 3 4 5 6 |
一个番石榴解决方案,完整性:
1 2 3 4 | import com.google.common.io.BaseEncoding; ... byte[] bytes ="Hello world".getBytes(StandardCharsets.UTF_8); final String hex = BaseEncoding.base16().lowerCase().encode(bytes); |
现在,
这个简单的一行程序适合我
使用数据类型转换器Class
以存储查找表的较小成本,此实现既简单又快速。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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); } |
对于固定长度,我将使用类似这样的方法,例如哈希:
1 |
我发现这里有三种不同的方式:网址:http://www.rgagnon.com/javadetails/java-0596.html
正如他所指出的,最优雅的是这个:
1 2 3 4 5 6 7 8 9 10 11 12 | 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(); } |
这个怎么样?
1 2 3 4 5 6 7 8 9 10 11 |
好的,有很多方法可以做到这一点,但是如果你决定使用一个库,我建议在你的项目中搜索一下,看看在添加一个新的库之前,这个库已经是你项目的一部分了。例如,如果你还没有
org.apache.commons.codec.binary.Hex
也许你真的…
org.apache.xerces.impl.dv.util.HexBin
我通常对debuf语句使用以下方法,但我不知道这是不是最好的方法
1 2 3 4 5 6 7 8 9 10 11 12 | 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(); } |
我喜欢用这个:
1 2 3 4 5 6 7 8 9 10 | 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); } |
它对公认答案的适应性稍有灵活性。就我个人而言,我既保留了公认的答案,也保留了这种超负荷的答案,在更多的上下文中都可以使用。
@maybewecouldsealavan提出的解决方案的一个小变种,它允许您在输出十六进制字符串中直观地将n个字节捆绑在一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(); } |
即:
1 2 3 4 5 | bytesToHexString("..DOOM..".toCharArray().getBytes(), 2); 2E2E 444F 4F4D 2E2E bytesToHexString("..DOOM..".toCharArray().getBytes(), 4); 2E2E444F 4F4D2E2E |
如果您使用的是Spring安全框架,那么可以使用:
1 2 3 4 5 |
在该页上找不到任何没有
这里有一个解决方案,它没有上述缺陷(但我的承诺没有其他缺陷)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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在ToString中有一个前缀为零的选项),那么"易于实现"的解决方案(如"字符串长度为奇数时使用零填充")将变得非常昂贵。
1 2 3 4 5 6 7 8 9 10 | 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); } |
我的解决方案是基于Maybwecouldstealavan的解决方案,但不依赖任何额外分配的查找表。它不使用任何"int-to-char"的casts-hack(实际上,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |
1 2 3 4 5 6 7 8 9 |
//移位字节更有效//你也可以用这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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(); } |
这是一个类似于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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=new Encoder(false); static final Encoder UPPER=new Encoder(true); } public static Encoder getEncoder(){ return Encoder.LOWER; } public static Encoder getUpperEncoder(){ return Encoder.UPPER; } //... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); } |
如果您正在寻找一个与Python完全一样的字节数组,那么我将Java实现转换为Python。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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=[]) |