在Java中,如何将字节数组转换为hex数字string,同时保持前导零?

我正在使用一些示例Java代码来制作MD5散列。 一部分将结果从字节转换为hex数字的string:

byte messageDigest[] = algorithm.digest(); StringBuffer hexString = new StringBuffer(); for (int i=0;i<messageDigest.length;i++) { hexString.append(Integer.toHexString(0xFF & messageDigest[i])); } 

然而,由于toStringString明显地减less了前导零,所以它不是很有效。 那么,从字节数组到hexstring保持前导零的最简单的方法是什么?

一个简单的方法是检查Integer.toHexString()输出多less个数字,并根据需要为每个字节添加一个前导零。 像这样的东西:

 public static String toHexString(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } 

查看Apache Commons Codec Hex.encodeHex 。 返回types是char[] ,可以平凡地转换为String 。 所以:

  String hexString = new String(Hex.encodeHex(messageDigest)); 

您可以使用下面的一个。 我用前导零字节和最初的负字节进行了testing

 public static String toHex(byte[] bytes) { BigInteger bi = new BigInteger(1, bytes); return String.format("%0" + (bytes.length << 1) + "X", bi); } 

如果您想使用小写hex数字,请在格式string中使用"x"

使用DatatypeConverter.printHexBinary() 。 您可以在http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html中阅读其文档;

例如:

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

将导致:

 000086003D 

我喜欢Steve提交的内容,但是他可以在没有几个variables的情况下完成,并保存了几行。

 public static String toHexString(byte[] bytes) { char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] hexChars = new char[bytes.length * 2]; int v; for ( int j = 0; j < bytes.length; j++ ) { v = bytes[j] & 0xFF; hexChars[j*2] = hexArray[v/16]; hexChars[j*2 + 1] = hexArray[v%16]; } return new String(hexChars); } 

我喜欢的是,很容易看到它在做什么(而不是依靠一些神奇的BigInteger黑盒转换),而且也不必担心诸如前置零和东西等拐angular情况。 这个例程需要每4位半字节变成一个hex字符。 它使用查表,所以它可能很快。 如果你用位移和AND代替v / 16和v%16,可能会更快,但我现在懒得去testing它。

我发现Integer.toHexString有点慢。 如果要转换很多字节,则可能需要考虑构build一个包含“00”,“FF”的string数组,并使用该整数作为索引。 即

 hexString.append(hexArray[0xFF & messageDigest[i]]); 

这是更快,并确保正确的长度。 只需要string数组:

 String[] hexArray = { "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"}; 

我一直在寻找同样的东西…在这里有一些好主意,但我跑了几个微基准。 我发现以下是最快的(从Ayman上面修改过来的,速度是上面的2倍,比Steve快上50%):

 public static String hash(String text, String algorithm) throws NoSuchAlgorithmException { byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes()); return new BigInteger(1, hash).toString(16); } 

编辑:哎呀 – 错过,这基本上是相同的kgiannakakis的,所以可能会剥离一个领先的0.仍然,修改这个以下,它仍然是最快的:

 public static String hash(String text, String algorithm) throws NoSuchAlgorithmException { byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes()); BigInteger bi = new BigInteger(1, hash); String result = bi.toString(16); if (result.length() % 2 != 0) { return "0" + result; } return result; } 
 static String toHex(byte[] digest) { StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%1$02X", b)); } return sb.toString(); } 
 String result = String.format("%0" + messageDigest.length + "s", hexString.toString()) 

这是给你已有的最短的解决scheme。 如果可以将字节数组转换为数字值,则String.format可以同时将其转换为hexstring。

这个解决scheme是一个小一点的学校,应该是记忆效率。

 public static String toHexString(byte bytes[]) { if (bytes == null) { return null; } StringBuffer sb = new StringBuffer(); for (int iter = 0; iter < bytes.length; iter++) { byte high = (byte) ( (bytes[iter] & 0xf0) >> 4); byte low = (byte) (bytes[iter] & 0x0f); sb.append(nibble2char(high)); sb.append(nibble2char(low)); } return sb.toString(); } private static char nibble2char(byte b) { byte nibble = (byte) (b & 0x0f); if (nibble < 10) { return (char) ('0' + nibble); } return (char) ('a' + nibble - 10); } 

另外一个select

 public static String toHexString(byte[]bytes) { StringBuilder sb = new StringBuilder(bytes.length*2); for(byte b: bytes) sb.append(Integer.toHexString(b+0x800).substring(1)); return sb.toString(); } 

为了保持领先的零,这里是保罗build议(例如MD5散列)的一个小的变化:

 public static String MD5hash(String text) throws NoSuchAlgorithmException { byte[] hash = MessageDigest.getInstance("MD5").digest(text.getBytes()); return String.format("%032x",new BigInteger(1, hash)); } 

哎呀,这看起来比艾曼build议的要差,对不起

 static String toHex(byte[] digest) { String digits = "0123456789abcdef"; StringBuilder sb = new StringBuilder(digest.length * 2); for (byte b : digest) { int bi = b & 0xff; sb.append(digits.charAt(bi >> 4)); sb.append(digits.charAt(bi & 0xf)); } return sb.toString(); } 

看起来concat和append函数可以真的很慢。 以下对我来说比我以前的post快得多。 在构build输出时更改为char数组是加快速度的关键因素。 我没有比较Brandon DuRettebuild议的Hex.encodeHex。

 public static String toHexString(byte[] bytes) { char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] hexChars = new char[10000000]; int c = 0; int v; for ( j = 0; j < bytes.length; j++ ) { v = bytes[j] & 0xFF; hexChars[c] = hexArray[v/16]; c++; hexChars[c] = hexArray[v%16]; c++; } return new String(hexChars, 0, c); } 

番石榴也使它非常简单:

 BaseEncoding.base16().encode( bytes ); 

当Apache Commons不可用时,这是一个很好的select。 它也有一些很好的控制输出,如:

 byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes ); // "0a:0b:0c:0d:0e:0f" 

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

 md5sum = String.format("%032x", new BigInteger(1, md.digest())); 

掩码中的0表示填充…

这是什么我用于MD5散列:

 public static String getMD5(String filename) throws NoSuchAlgorithmException, IOException { MessageDigest messageDigest = java.security.MessageDigest.getInstance("MD5"); InputStream in = new FileInputStream(filename); byte [] buffer = new byte[8192]; int len = in.read(buffer, 0, buffer.length); while (len > 0) { messageDigest.update(buffer, 0, len); len = in.read(buffer, 0, buffer.length); } in.close(); return new BigInteger(1, messageDigest.digest()).toString(16); } 

编辑:我已经testing,我已经注意到,这也尾随零被切断。 但是,这只能在一开始就发生,所以你可以比较预期的长度和相应的垫。

没有外部库,你可以less写:

 String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes())) 

这个解决scheme不需要位移或屏蔽,查找表或外部库,并且大概是我可以得到的最短的:

 byte[] digest = new byte[16]; Formatter fmt = new Formatter(); for (byte b : digest) { fmt.format("%02X", b); } fmt.toString() 
 byte messageDigest[] = algorithm.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < messageDigest.length; i++) { String hexByte = Integer.toHexString(0xFF & messageDigest[i]); int numDigits = 2 - hexByte.length(); while (numDigits-- > 0) { hexString.append('0'); } hexString.append(hexByte); } 

恕我直言,以上提供片段删除前导零的所有解决scheme都是错误的。

 byte messageDigest[] = algorithm.digest(); for (int i = 0; i < messageDigest.length; i++) { hexString.append(Integer.toHexString(0xFF & messageDigest[i])); } 

根据这段代码,在迭代中从字节数组中获取8位,转换为整数(因为Integer.toHexString函数将int作为参数),然后将该整数转换为相应的散列值。 因此,例如,如果您的二进制文件中包含00000001 00000001,则根据代码,hexStringvariables将具有0x11作为hex值,而正确的值应该是0x0101。 因此,在计算MD5时,我们可能得到长度小于32字节的散列(因为缺less零),这可能不满足MD5散列所做的密码唯一属性。

解决问题的办法是用下面的代码片段代替上面的代码片段:

 byte messageDigest[] = algorithm.digest(); for (int i = 0; i < messageDigest.length; i++) { int temp=0xFF & messageDigest[i]; String s=Integer.toHexString(temp); if(temp<=0x0F){ s="0"+s; } hexString.append(s); } 

这也是相当的,但更简洁的使用Apache util HexBin的代码减less到

 HexBin.encode(messageDigest).toLowerCase(); 

这会给一个字节两个字符的长string。

 public String toString(byte b){ final char[] Hex = new String("0123456789ABCDEF").toCharArray(); return "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)]; } 

你怎么能再从ASCII转换回字节数组?

我遵循以下代码转换为由Jemenake给出的ascii。

 public static String toHexString(byte[] bytes) { char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] hexChars = new char[bytes.length * 2]; int v; for ( int j = 0; j < bytes.length; j++ ) { v = bytes[j] & 0xFF; hexChars[j*2] = hexArray[v/16]; hexChars[j*2 + 1] = hexArray[v%16]; } return new String(hexChars); } 

我的变种

  StringBuilder builder = new StringBuilder(); for (byte b : bytes) { builder.append(Character.forDigit(b/16, 16)); builder.append(Character.forDigit(b % 16, 16)); } System.out.println(builder.toString()); 

这个对我有用。

这是一个错误的解决scheme? (android java)

  // Create MD5 Hash MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(s.getBytes()); byte[] md5sum = digest.digest(); BigInteger bigInt = new BigInteger(1, md5sum); String stringMD5 = bigInt.toString(16); // Fill to 32 chars stringMD5 = String.format("%32s", stringMD5).replace(' ', '0'); return stringMD5; 

所以基本上它用0代替空格。

我很惊讶,没有人提出以下解决scheme:

 StringWriter sw = new StringWriter(); com.sun.corba.se.impl.orbutil.HexOutputStream hex = new com.sun.corba.se.impl.orbutil.HexOutputStream(sw); hex.write(byteArray); System.out.println(sw.toString());