¿Convertir una representación de cadena de un volcado hexadecimal a una matriz de bytes utilizando Java?
hexadecimal to bytes java (25)
De una sola línea:
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 de ustedes interesados en el código real detrás de los One-liners de FractalizeR (necesitaba que, dado que javax.xml.bind no está disponible para Android (por defecto)), esto viene 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<len; i+=2 ) {
int h = hexToBin(s.charAt(i ));
int l = hexToBin(s.charAt(i+1));
if( h==-1 || l==-1 )
throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);
out[i/2] = (byte)(h*16+l);
}
return out;
}
private static int hexToBin( char ch ) {
if( ''0''<=ch && ch<=''9'' ) return ch-''0'';
if( ''A''<=ch && ch<=''F'' ) return ch-''A''+10;
if( ''a''<=ch && ch<=''f'' ) return ch-''a''+10;
return -1;
}
private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
public String printHexBinary(byte[] data) {
StringBuilder r = new StringBuilder(data.length*2);
for ( byte b : data) {
r.append(hexCode[(b >> 4) & 0xF]);
r.append(hexCode[(b & 0xF)]);
}
return r.toString();
}
Estoy buscando una manera de convertir una cadena larga (de un volcado), que representa los valores hexadecimales en una matriz de bytes.
No podría haberlo expresado mejor que la persona que publicó la misma pregunta aquí .
Pero para mantenerlo original, lo expresaré a mi manera: supongamos que tengo una cadena "00A0BF"
que me gustaría interpretar como la
byte[] {0x00,0xA0,0xBf}
¿Qué tengo que hacer?
Soy un principiante de Java y terminé usando BigInteger
y cuidando los ceros hexadecimales principales. Pero creo que es feo y estoy seguro de que me estoy perdiendo algo simple.
Ahora puedes usar BaseEncoding en guava
para lograr esto.
BaseEncoding.base16().decode(string);
Para revertirlo use
BaseEncoding.base16().encode(bytes);
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; llamando aCharArray () hace una copia de la matriz interna de la cadena.
EDIT: Ah, y por cierto, los bytes están firmados en Java, por lo que su cadena de entrada se convierte a [0, -96, -65] en lugar de [0, 160, 191]. Pero probablemente ya lo sabías.
Aquí hay una solución que creo que es mejor que cualquier otra publicada hasta ahora:
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 que es una mejora:
Seguro con ceros iniciales (a diferencia de BigInteger) y con valores de byte 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 bibliotecas que puedan no estar disponibles.
Siéntase libre de agregar la verificación de argumentos a través de assert
o de excepciones si se sabe que el argumento no es seguro.
Creo que lo haré por ti. Lo armé 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;
}
EDITAR: según lo señalado por @mmyers, este método no funciona en la entrada que contiene las subcadenas correspondientes a los bytes con el bit alto establecido ("80" - "FF"). La explicación se encuentra en el Id. De 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 HexBinaryAdapter
proporciona la capacidad de calcular y unmarshal 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;
}
Ese es solo un ejemplo que escribí ... En realidad, lo uso tal como está y no necesito crear un método separado para usarlo.
El Código presentado por Bert Regelink simplemente no funciona. Intenta 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);
}
}
El método BigInteger()
de java.math es muy lento y no es recomendable.
Integer.parseInt(HEXString, 16)
puede causar problemas con algunos caracteres sin convertir a Digit / Integer
un buen método de trabajo:
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
En Android, si está trabajando con hexadecimal, puede probar okio .
uso simple:
byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();
y el resultado será
[-64, 0, 6, 0, 0]
En realidad, creo que la solución BigInteger es muy buena:
new BigInteger("00A0BF", 16).toByteArray();
Edición: No es seguro para los ceros iniciales , como lo señala el cartel.
Encontré que Kernel Panic tenía la solución más útil para mí, pero tuve problemas si la cadena hexadecimal era un número impar. Resuelto 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<len;i+=2)
{
int hh = hexVal.indexOf(st.charAt(i));
if (hh == -1) hh = hexValL.indexOf(st.charAt(i));
int lh = hexVal.indexOf(st.charAt(i+1));
if (lh == -1) lh = hexValL.indexOf(st.charAt(i+1));
out[(i/2)+1] = (byte)((hh << 4)|lh);
}
return (len/2)+1;
}
Estoy agregando un número de números hexadecimales a una matriz, así que paso la referencia a la matriz que estoy usando, y el int Necesito convertir y devolver la posición relativa del próximo número hexadecimal. Así que la matriz de bytes final tiene [0] número de pares de hexágonos, [1 ...] pares de hexágonos, luego el número de pares ...
La clase Hex en commons-codec debería hacer eso por ti.
http://commons.apache.org/codec/
import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
Me gusta la solución Character.digit, pero aquí es cómo la 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;
}
Mi solución formal:
/**
* Decodes a hexadecimally encoded binary string.
* <p>
* Note that this function does <em>NOT</em> 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"
Para 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;
}
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;
}
Por lejos no es 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<msg.length ; i++){
currentIndex++;
if(i == 0){
result += String.format("/n %04x ", i);
}
if(i % 16 == 0 && i != 0){
result += " | ";
for(int j=(i-16) ; j<msg.length && j<i ; j++) {
char characterToAdd = (char) msg[j];
if (characterToAdd == ''/n'') {
characterToAdd = '' '';
}
result += characterToAdd;
}
result += String.format("/n %04x ", i);
}
result += String.format("%02x ", msg[i]);
}
if(currentIndex % 16 != 0){
int fitIns = msg.length / 16;
int leftOvers = msg.length - (fitIns * 16);
for(int i=0 ; i<16-leftOvers ; i++){
result += " ";
}
result += " | ";
for(int i=msg.length-leftOvers ; i<msg.length ; i++){
char characterToAdd = (char) msg[i];
if (characterToAdd == ''/n'') {
characterToAdd = '' '';
}
result += characterToAdd;
}
}
result += "/n";
return result;
}
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 | ?
Sé que este es un hilo muy viejo, pero todavía me gusta agregar mi valor de centavo.
Si realmente necesito codificar una simple cadena hexadecimal a convertidor binario, me gustaría hacerlo de la siguiente manera.
public static byte[] hexToBinary(String s){
/*
* skipped any input validation code
*/
byte[] data = new byte[s.length()/2];
for( int i=0, j=0;
i<s.length() && j<data.length;
i+=2, j++)
{
data[j] = (byte)Integer.parseInt(s.substring(i, i+2), 16);
}
return data;
}
Si tiene una preferencia por las secuencias de Java 8 como su estilo de codificación, entonces esto puede lograrse utilizando solo primitivos 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 concatenar colector pueden omitirse si no le importa capturar IOException
.
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 cualquier otro criterio, como en agrupaciones de dos caracteres.
Sobre la base de la solución op votada, 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 char ahorra las comprobaciones de longitud en charAt
Tarde en la fiesta, pero he amalgamado la respuesta anterior por DaveL en una clase con la acción inversa, por si acaso 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 prueba de clase 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 una sola línea:
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 de raíz java.se (predeterminado), por lo que resultará en 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 notarlo), pero puede tomar el código fuente si su sistema carece dejavax.xml
por alguna razón. Gracias a @Bert Regelink
por extraer la fuente.
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<sHex.length()/2;i++) {
ba[i] = (Integer.decode(
"0x"+sHex.substring(i*2, (i+1)*2))).byteValue();
}
return ba;
}