¿Cómo convertir una matriz de bytes a una cadena hexadecimal en Java?

Tengo una matriz de bytes llena de números hexadecimales e imprimirla de la manera fácil es bastante inútil porque hay muchos elementos no imprimibles. Lo que necesito es el hexcode exacto en forma de: 3a5f771c

De la discusión aquí , y especialmente esta respuesta, esta es la función que uso actualmente:

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

Mis propios pequeños puntos de referencia (un millón de bytes por mil veces, 256 bytes 10 millones de veces) mostraron que es mucho más rápido que cualquier otra alternativa, la mitad del tiempo en arreglos largos. Comparado con la respuesta de la que lo tomé, al cambiar a operaciones en modo bit, como se sugirió en la discusión, reduje aproximadamente un 20% del tiempo para arreglos largos. (Editar: Cuando digo que es más rápido que las alternativas, me refiero al código alternativo que se ofrece en las discusiones. El rendimiento es equivalente a Commons Codec, que usa un código muy similar).

La biblioteca Apache Commons Codec tiene una clase Hex para hacer solo este tipo de trabajo.

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

Utilice DatatypeConverter.printHexBinary() . Puede leer su documentación en http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html

Por ejemplo:

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

Resultará en:

 000086003D 

Como puede ver, recuperará la cadena hexadecimal que representa la matriz de bytes con ceros a la izquierda.

Esta respuesta es básicamente la misma que en la pregunta En Java, ¿cómo convierto una matriz de bytes a una cadena de dígitos hexadecimales mientras mantengo los ceros a la izquierda?

La solución más simple, sin libs externos, sin constantes de dígitos:

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

Una solución de guayaba, para completar:

 import com.google.common.io.BaseEncoding; ... byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8); final String hex = BaseEncoding.base16().lowerCase().encode(bytes); 

Ahora hex es "48656c6c6f20776f726c64" .

Este simple oneliner funciona para mí
String result = new BigInteger(1, inputBytes).toString(16);
EDITAR – Usar esto eliminará los ceros a la izquierda, pero funcionó para mi caso de uso. Gracias @Voicu por señalarlo

Usar la clase DataTypeConverter javax.xml.bind.DataTypeConverter

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

Encontré tres formas diferentes aquí: http://www.rgagnon.com/javadetails/java-0596.html

El más elegante, como también señala, creo que es este:

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

Con el menor costo de almacenamiento de la tabla de búsqueda, esta implementación es simple y muy rápida.

  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 

Usaría algo como esto para longitud fija, como hashes:

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

¿Qué tal esto?

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

Prefiero usar esto:

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

Es una adaptación un poco más flexible de la respuesta aceptada. Personalmente, mantengo la respuesta aceptada y esta sobrecarga junto con ella, utilizable en más contextos.

Normalmente utilizo el siguiente método para la statement debuf, pero no sé si es la mejor manera de hacerlo o no.

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

Ok, hay muchas maneras de hacerlo, pero si decides usar una biblioteca, te sugiero que revises en tu proyecto para ver si se ha implementado algo en una biblioteca que ya es parte de tu proyecto antes de agregar una nueva biblioteca. solo para hacer esto Por ejemplo, si todavía no tienes

org.apache.commons.codec.binary.Hex

tal vez lo tienes …

org.apache.xerces.impl.dv.util.HexBin

Una pequeña variante de la solución propuesta por @maybewecouldstealavan, que le permite agrupar visualmente N bytes en la cadena hexadecimal de salida:

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

Es decir:

 bytesToHexString("..DOOM..".toCharArray().getBytes(), 2); 2E2E 444F 4F4D 2E2E bytesToHexString("..DOOM..".toCharArray().getBytes(), 4); 2E2E444F 4F4D2E2E 

No se puede encontrar ninguna solución en esta página que no

  1. Use un bucle
  2. Use javax.xml.bind.DatatypeConverter que comstack bien pero a menudo arroja java.lang.NoClassDefFoundError en tiempo de ejecución.

Aquí hay una solución que no tiene los defectos anteriores (sin promesas, la mía no tiene otros defectos)

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

No pude obtener esto bajo 62 códigos de operación, pero si puede vivir sin relleno en caso de que el primer byte sea menor a 0x10, entonces la siguiente solución solo usa 23 códigos de operación. Realmente muestra cómo las soluciones “fáciles de implementar” como “pad con cero si la longitud de la cadena es impar” pueden ser bastante costosas si una implementación nativa no está disponible (o en este caso, si BigInteger tenía una opción de prefijar con ceros en Encadenar).

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

// Cambiar los bytes es más eficiente // Puedes usar este también

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

Si está buscando una matriz de bytes exactamente como esta para python, he convertido esta implementación de Java en python.

 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=[]) 
  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; } 

Aquí hay una implementación tipo java.util.Base64 (parcial), ¿no es lindo?

 public class Base16/*aka 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>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; } //... } 
 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(); } 

Mi solución se basa en la solución de maybeWeCouldStealAVan, pero no depende de ninguna tabla de búsqueda adicional asignada. No utiliza ningún hack de conversión ‘int-to-char’ (en realidad, Character.forDigit() hace, realizando algunas comparaciones para verificar realmente qué es el dígito) y, por lo tanto, puede ser un poco más lento. Por favor, siéntete libre de usarlo donde quieras. Aclamaciones.

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