Convierta una representación de cadena de un volcado hexadecimal a una matriz de bytes utilizando Java?

Estoy buscando una forma de convertir una cadena larga (de un volcado), que represente valores hexadecimales en una matriz de bytes.

No podría haberlo redactado mejor que la persona que publicó la misma pregunta aquí .

Pero para mantenerlo original, lo voy a decir a mi manera: supongamos que tengo una cuerda "00A0BF" que me gustaría interpretar como el

 byte[] {0x00,0xA0,0xBf} 

¿que debería hacer?

Soy un principiante de Java y terminé usando BigInteger y BigInteger a los principales ceros hexadecimales. Pero creo que es feo y estoy seguro de que me falta algo simple.

Aquí hay una solución que creo que es mejor que cualquiera publicada hasta el momento:

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

Razones por las cuales es una mejora:

  • Seguro con ceros a la izquierda (a diferencia de BigInteger) y con valores de bytes negativos (a diferencia de Byte.parseByte)

  • No convierte el String en un char[] , ni crea objetos StringBuilder y String para cada byte.

  • No hay dependencias de biblioteca que pueden no estar disponibles

Siéntase libre de agregar la verificación de argumentos a través de assert o excepciones si no se sabe que el argumento es seguro.

One-liners:

 import javax.xml.bind.DatatypeConverter; public static String toHexString(byte[] array) { return DatatypeConverter.printHexBinary(array); } public static byte[] toByteArray(String s) { return DatatypeConverter.parseHexBinary(s); } 

Advertencias :

  • en Java 9 Jigsaw esto ya no forma parte del conjunto raíz (predeterminado) java.se, por lo que dará como resultado una excepción ClassNotFoundException a menos que especifique –add-modules java.se.ee (gracias a @ eckes )
  • No está disponible en Android (gracias a Fabian por anotar eso), pero puede tomar el código fuente si su sistema carece de javax.xml por alguna razón. Gracias a @ Bert Regelink por extraer la fuente.

La clase Hex en commons-codec debería hacer eso por ti.

http://commons.apache.org/codec/

Ahora puede usar BaseEncoding en guava para lograr esto.

 BaseEncoding.base16().decode(string); 

Para revertir el uso

 BaseEncoding.base16().encode(bytes); 

En realidad, creo que la solución de BigInteger es muy buena:

 new BigInteger("00A0BF", 16).toByteArray(); 

Editar: No es seguro para los ceros a la izquierda , como se indica en el póster.

HexBinaryAdapter proporciona la capacidad de reunir y desenmascarar entre String y byte[] .

 import javax.xml.bind.annotation.adapters.HexBinaryAdapter; public byte[] hexToBytes(String hexString) { HexBinaryAdapter adapter = new HexBinaryAdapter(); byte[] bytes = adapter.unmarshal(hexString); return bytes; } 

Es solo un ejemplo que escribí … De hecho, lo uso como está y no necesito hacer un método diferente para usarlo.

One-liners:

 import javax.xml.bind.DatatypeConverter; public static String toHexString(byte[] array) { return DatatypeConverter.printHexBinary(array); } public static byte[] toByteArray(String s) { return DatatypeConverter.parseHexBinary(s); } 

Para aquellos que estén interesados ​​en el código real detrás de One-liners de FractalizeR (lo necesitaba porque javax.xml.bind no está disponible para Android (de manera predeterminada)), esto proviene de com.sun.xml.internal.bind. DatatypeConverterImpl.java :

 public byte[] parseHexBinary(String s) { final int len = s.length(); // "111" is not a valid hex encoding. if( len%2 != 0 ) throw new IllegalArgumentException("hexBinary needs to be even-length: "+s); byte[] out = new byte[len/2]; for( int i=0; i> 4) & 0xF]); r.append(hexCode[(b & 0xF)]); } return r.toString(); } 

Aquí hay un método que realmente funciona (basado en varias respuestas semi-correctas anteriores):

 private static byte[] fromHexString(final String encoded) { if ((encoded.length() % 2) != 0) throw new IllegalArgumentException("Input string must contain an even number of characters"); final byte result[] = new byte[encoded.length()/2]; final char enc[] = encoded.toCharArray(); for (int i = 0; i < enc.length; i += 2) { StringBuilder curr = new StringBuilder(2); curr.append(enc[i]).append(enc[i + 1]); result[i/2] = (byte) Integer.parseInt(curr.toString(), 16); } return result; } 

El único problema posible que puedo ver es si la cadena de entrada es extremadamente larga; la llamada a Charray () hace una copia de la matriz interna de la cadena.

EDITAR: Ah, y por cierto, los bytes están firmados en Java, por lo que su cadena de entrada se convierte en [0, -96, -65] en lugar de [0, 160, 191]. Pero probablemente ya lo sabías.

En Android, si estás trabajando con hexadecimal, puedes probar okio .

uso simple:

 byte[] bytes = ByteString.decodeHex("c000060000").toByteArray(); 

y el resultado será

 [-64, 0, 6, 0, 0] 

EDITAR: como señala @mmyers, este método no funciona en la entrada que contiene subcadenas correspondientes a bytes con el bit alto establecido (“80” – “FF”). La explicación está en el ID del error: 6259307 Byte.parseByte no funciona como se anuncia en la documentación del SDK .

 public static final byte[] fromHexString(final String s) { byte[] arr = new byte[s.length()/2]; for ( int start = 0; start < s.length(); start += 2 ) { String thisByte = s.substring(start, start+2); arr[start/2] = Byte.parseByte(thisByte, 16); } return arr; } 

El Código presentado por Bert Regelink simplemente no funciona. Pruebe lo siguiente:

 import javax.xml.bind.DatatypeConverter; import java.io.*; public class Test { @Test public void testObjectStreams( ) throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); String stringTest = "TEST"; oos.writeObject( stringTest ); oos.close(); baos.close(); byte[] bytes = baos.toByteArray(); String hexString = DatatypeConverter.printHexBinary( bytes); byte[] reconvertedBytes = DatatypeConverter.parseHexBinary(hexString); assertArrayEquals( bytes, reconvertedBytes ); ByteArrayInputStream bais = new ByteArrayInputStream(reconvertedBytes); ObjectInputStream ois = new ObjectInputStream(bais); String readString = (String) ois.readObject(); assertEquals( stringTest, readString); } } 

Por lo que vale, aquí hay otra versión que admite cadenas de longitud impar, sin recurrir a la concatenación de cadenas.

 public static byte[] hexStringToByteArray(String input) { int len = input.length(); if (len == 0) { return new byte[] {}; } byte[] data; int startIdx; if (len % 2 != 0) { data = new byte[(len / 2) + 1]; data[0] = (byte) Character.digit(input.charAt(0), 16); startIdx = 1; } else { data = new byte[len / 2]; startIdx = 0; } for (int i = startIdx; i < len; i += 2) { data[(i + 1) / 2] = (byte) ((Character.digit(input.charAt(i), 16) << 4) + Character.digit(input.charAt(i+1), 16)); } return data; } 

Siempre he usado un método como

 public static final byte[] fromHexString(final String s) { String[] v = s.split(" "); byte[] arr = new byte[v.length]; int i = 0; for(String val: v) { arr[i++] = Integer.decode("0x" + val).byteValue(); } return arr; } 

este método se divide en valores hexadecimales delimitados por espacios, pero no sería difícil dividir la cadena en ningún otro criterio, como en grupos de dos caracteres.

El método BigInteger() de java.math es muy lento y no recomendable.

Integer.parseInt(HEXString, 16)

puede causar problemas con algunos caracteres sin convertir a Digit / Integer

un método de trabajo de pozo:

 Integer.decode("0xXX") .byteValue() 

Función:

 public static byte[] HexStringToByteArray(String s) { byte data[] = new byte[s.length()/2]; for(int i=0;i < s.length();i+=2) { data[i/2] = (Integer.decode("0x"+s.charAt(i)+s.charAt(i+1))).byteValue(); } return data; } 

Diviértete, buena suerte

Me gusta la solución Character.digit, pero así es como lo resolví

 public byte[] hex2ByteArray( String hexString ) { String hexVal = "0123456789ABCDEF"; byte[] out = new byte[hexString.length() / 2]; int n = hexString.length(); for( int i = 0; i < n; i += 2 ) { //make a bit representation in an int of the hex value int hn = hexVal.indexOf( hexString.charAt( i ) ); int ln = hexVal.indexOf( hexString.charAt( i + 1 ) ); //now just shift the high order nibble and add them together out[i/2] = (byte)( ( hn << 4 ) | ln ); } return out; } 

Encontré que Kernel Panic tenía la solución más útil para mí, pero me encontré con problemas si la cadena hexadecimal era un número impar. lo resolvió de esta manera:

 boolean isOdd(int value) { return (value & 0x01) !=0; } private int hexToByte(byte[] out, int value) { String hexVal = "0123456789ABCDEF"; String hexValL = "0123456789abcdef"; String st = Integer.toHexString(value); int len = st.length(); if (isOdd(len)) { len+=1; // need length to be an even number. st = ("0" + st); // make it an even number of chars } out[0]=(byte)(len/2); for (int i =0;i 

Estoy agregando un número de números hexadecimales a una matriz, por lo que paso la referencia a la matriz que estoy usando, y la int que necesito convertir y devolver la posición relativa del siguiente número hexadecimal. Entonces, el conjunto de bytes final tiene [0] número de pares hexadecimales, [1 ...] pares hexadecimales, luego el número de pares ...

En función de la solución optativa, lo siguiente debería ser un poco más eficiente:

  public static byte [] hexStringToByteArray (final String s) { if (s == null || (s.length () % 2) == 1) throw new IllegalArgumentException (); final char [] chars = s.toCharArray (); final int len = chars.length; final byte [] data = new byte [len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit (chars[i], 16) << 4) + Character.digit (chars[i + 1], 16)); } return data; } 

Porque: la conversión inicial a una matriz de caracteres perdona las comprobaciones de longitud en charAt

Si tiene preferencia por las secuencias Java 8 como su estilo de encoding, esto se puede lograr utilizando solo primitivas JDK.

 String hex = "0001027f80fdfeff"; byte[] converted = IntStream.range(0, hex.length() / 2) .map(i -> Character.digit(hex.charAt(i * 2), 16) < < 4 | Character.digit(hex.charAt((i * 2) + 1), 16)) .collect(ByteArrayOutputStream::new, ByteArrayOutputStream::write, (s1, s2) -> s1.write(s2.toByteArray(), 0, s2.size())) .toByteArray(); 

Los parámetros , 0, s2.size() en la función de concatenación del recostackdor se pueden omitir si no le importa capturar IOException .

 public static byte[] hex2ba(String sHex) throws Hex2baException { if (1==sHex.length()%2) { throw(new Hex2baException("Hex string need even number of chars")); } byte[] ba = new byte[sHex.length()/2]; for (int i=0;i 

Mi solución formal:

 /** * Decodes a hexadecimally encoded binary string. * 

* Note that this function does NOT convert a hexadecimal number to a * binary number. * * @param hex Hexadecimal representation of data. * @return The byte[] representation of the given data. * @throws NumberFormatException If the hexadecimal input string is of odd * length or invalid hexadecimal string. */ public static byte[] hex2bin(String hex) throws NumberFormatException { if (hex.length() % 2 > 0) { throw new NumberFormatException("Hexadecimal input string must have an even length."); } byte[] r = new byte[hex.length() / 2]; for (int i = hex.length(); i > 0;) { r[i / 2 - 1] = (byte) (digit(hex.charAt(--i)) | (digit(hex.charAt(--i)) < < 4)); } return r; } private static int digit(char ch) { int r = Character.digit(ch, 16); if (r < 0) { throw new NumberFormatException("Invalid hexadecimal string: " + ch); } return r; }

Es como la función PHP hex2bin () pero en estilo Java.

Ejemplo:

 String data = new String(hex2bin("6578616d706c65206865782064617461")); // data value: "example hex data" 

Tarde en la fiesta, pero he amalgamado la respuesta anterior de DaveL a una clase con la acción inversa, por si acaso me ayuda.

 public final class HexString { private static final char[] digits = "0123456789ABCDEF".toCharArray(); private HexString() {} public static final String fromBytes(final byte[] bytes) { final StringBuilder buf = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { buf.append(HexString.digits[(bytes[i] >> 4) & 0x0f]); buf.append(HexString.digits[bytes[i] & 0x0f]); } return buf.toString(); } public static final byte[] toByteArray(final String hexString) { if ((hexString.length() % 2) != 0) { throw new IllegalArgumentException("Input string must contain an even number of characters"); } final int len = hexString.length(); final byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return data; } } 

Y clase de prueba JUnit:

 public class TestHexString { @Test public void test() { String[] tests = {"0FA1056D73", "", "00", "0123456789ABCDEF", "FFFFFFFF"}; for (int i = 0; i < tests.length; i++) { String in = tests[i]; byte[] bytes = HexString.toByteArray(in); String out = HexString.fromBytes(bytes); System.out.println(in); //DEBUG System.out.println(out); //DEBUG Assert.assertEquals(in, out); } } } 

De lejos, la solución más limpia. Pero funciona para mí y está bien formateado:

 private String createHexDump(byte[] msg, String description) { System.out.println(); String result = "\n" + description; int currentIndex = 0; for(int i=0 ; i 

La salida:

  S -> C 0000 0b 00 2e 06 4d 6f 72 69 74 7a 53 6f 6d 65 20 54 | .Heyyy Some T 0010 43 50 20 73 74 75 66 66 20 49 20 63 61 70 74 75 | CP stuff I captu 0020 72 65 64 2e 2e 77 65 6c 6c 20 66 6f 72 6d 61 74 | red..well format 0030 3f | ? 

Creo que lo hará por ti. Lo improvisé a partir de una función similar que devolvió los datos como una cadena:

 private static byte[] decode(String encoded) { byte result[] = new byte[encoded/2]; char enc[] = encoded.toUpperCase().toCharArray(); StringBuffer curr; for (int i = 0; i < enc.length; i += 2) { curr = new StringBuffer(""); curr.append(String.valueOf(enc[i])); curr.append(String.valueOf(enc[i + 1])); result[i] = (byte) Integer.parseInt(curr.toString(), 16); } return result; } 

Para mí esta fue la solución, HEX = “FF01” luego se dividió en FF (255) y 01 (01)

 private static byte[] BytesEncode(String encoded) { //System.out.println(encoded.length()); byte result[] = new byte[encoded.length() / 2]; char enc[] = encoded.toUpperCase().toCharArray(); String curr = ""; for (int i = 0; i < encoded.length(); i=i+2) { curr = encoded.substring(i,i+2); System.out.println(curr); if(i==0){ result[i]=((byte) Integer.parseInt(curr, 16)); }else{ result[i/2]=((byte) Integer.parseInt(curr, 16)); } } return result; }