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

649

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

Andre
fuente
12
¿Por qué no probarlo primero y mostrarnos lo que tienes? No tienes nada que perder y todo que ganar. Integer tiene un toHexString(...)método que puede ayudar si esto es lo que estás buscando. También String.format(...)puede hacer algunos trucos de formateo con la %2xcadena de código.
Hovercraft Full Of Eels
"Lo que necesito es el código hexadecimal exacto en forma de: 3a5f771c ..." - solicitó una forma exacta, pero no proporcionó un ejemplo exacto. Continuando con lo que proporcionó, convierta los primeros cuatro bytes en una cadena, luego concatene las elipses en la cadena.
jww
1
Con la ayuda de stream en Java 8, se puede implementar simplemente como: static String byteArrayToHex (byte [] a) {return IntStream.range (0, a.length) .mapToObj (i -> String.format ("% 02x ", a [i])) .reduce ((acc, v) -> acc +" "+ v) .get (); }
tibetty

Respuestas:

901

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

private static final char[] HEX_ARRAY = "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] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

Mis propios puntos de referencia pequeños (un millón de bytes mil veces, 256 bytes 10 millones de veces) demostraron que es mucho más rápido que cualquier otra alternativa, aproximadamente la mitad del tiempo en matrices largas. En comparación con la respuesta que obtuve, cambiar a operaciones bit a bit, como se sugirió en la discusión, redujo aproximadamente un 20% del tiempo para las matrices largas. (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).

Versión 2k20, con respecto a las cadenas compactas de Java 9:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".toByteArray();
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars, StandardCharsets.UTF_8);
}
maybeWeCouldStealAVan
fuente
266
Acabo de encontrar javax.xml.bind.DataTypeConverter , parte de la distribución estándar. ¿Por qué no surge esto cuando buscas este tipo de problema en Google? Muchas herramientas útiles, incluidas String printHexBinary(byte[])y byte[] parseHexBinary(String). printHexBinaryes, sin embargo, mucho (2x) más lento que la función en esta respuesta. (Verifiqué la fuente; usa a stringBuilder. parseHexBinaryUsa una matriz.) Realmente, sin embargo, para la mayoría de los propósitos es lo suficientemente rápido y probablemente ya lo tenga.
maybeWeCouldStealAVan
75
+1 para la respuesta ya que Android no tiene DataTypeConverter
Vaiden
77
@maybeWeCouldStealAVan: JDK 7 ahora es de código abierto. ¿Deberíamos enviar un parche para mejorar el rendimiento printHexBinary?
kevinarpe
3
@maybeWeCouldStealAVan ¿podría explicar cómo funciona esto? Lo sigo en su mayor parte, pero realmente me gusta entender lo que sucede cuando uso el código. ¡Gracias!
jjNford
24
javax.xml.bind.DataTypeConverterestá siendo eliminado de Java 11.
The Impaler
421

La biblioteca de códecs de Apache Commons tiene una clase Hex para hacer 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 ) );
chooban
fuente
12
@cytinus: mi voto negativo ocurrió hace 4 meses, así que no estoy completamente seguro de lo que estaba pensando, pero probablemente estaba objetando el tamaño de la biblioteca. Esta es una pequeña función dentro del programa; no es necesario agregar una biblioteca tan voluminosa al proyecto para realizarla.
ArtOfWarfare
66
@ArtOfWarefare Estoy de acuerdo, así que en lugar de import org.apache.commons.codec.*;usted podría hacerloimport org.apache.commons.codec.binary.Hex;
cytinus
12
@ArtOfWarfare Tengo que estar en desacuerdo. Lo único terrible es que las bibliotecas de apache commons no se incluyen por defecto con JRE y JDK. Hay algunas bibliotecas que son tan útiles que realmente deberían estar en su ruta de clase por defecto, y esta es una de ellas.
corsiKa
29
Recomiendo encarecidamente que esta respuesta se cambie como la respuesta principal. Siempre vote para usar una biblioteca de código abierto, bien probada y de rendimiento sobre código personalizado que no mejore.
Dmitriy Likhten
66
O en caso de que use BouncyCastle ( org.bouncycastle: bcprov-jdk15on ), puede usar esta clase:, org.bouncycastle.util.encoders.Hexcon este método:String toHexString(byte[] data)
Guillaume Husta
320

El método javax.xml.bind.DatatypeConverter.printHexBinary(), parte de la Arquitectura Java para Enlace XML (JAXB) , era una forma conveniente de convertir una byte[]cadena hexadecimal. La DatatypeConverterclase también incluyó muchos otros métodos útiles de manipulación de datos.

En Java 8 y versiones anteriores, JAXB era parte de la biblioteca estándar de Java. Se desaprobó con Java 9 y se eliminó con Java 11 , como parte de un esfuerzo por mover todos los paquetes Java EE a sus propias bibliotecas. Es una larga historia . Ahora, javax.xml.bindno existe, y si desea utilizar JAXB, que contiene DatatypeConverter, deberá instalar la API JAXB y JAXB Runtime de Maven.

Ejemplo de uso:

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

Resultará en:

000086003D

Esta respuesta es la misma que esta .

PhoneixS
fuente
13
Una buena solución, aunque lamentablemente no es válida en Android.
Kazriko
@Kazriko tal vez quieras leer code.google.com/p/dalvik/wiki/JavaxPackages . Es una forma de obtener clases de Java en Android. Pero si solo quieres convertir a hexadecimal, no vale la pena.
PhoneixS
13
DatatypeConverter ya no es accesible a partir de JDK 9
pmcollins
3
@PhoneixS Todavía está allí, pero no forma parte del tiempo de ejecución predeterminado (debido a los módulos Java 9).
Spotlight
2
no confíe en javax.xml.bind, se compila bien pero no se puede encontrar en tiempo de ejecución. si lo hace, prepárese para manejar java.lang.NoClassDefFoundError
Dmitry
227

La solución más simple, sin libs externas, 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();
}
Puntero nulo
fuente
14
Esto es muy lento, en promedio 1000 veces más lento (para 162 bytes de longitud) que el de la respuesta superior. Evite usar String.Format si el rendimiento es importante.
pt123
8
Quizás lento. Es bueno para cosas que suceden ocasionalmente, como inicio de sesión o similar.
Puntero nulo
29
Si es lento, ¿y qué? En mi caso de uso es solo para una declaración de depuración, así que gracias por este fragmento de código.
vikingsteve
8
Reutilizar una biblioteca al incluir archivos JAR adicionales de varias docenas de KB no sería exactamente eficiente si todo lo que necesita es esta función (en algunas plataformas como Android, todo el Jar se incluye en la aplicación final). Y a veces un código más corto y más claro es mejor cuando no se necesita rendimiento.
personne3000
2
@ personne3000 tal vez, pero en ese caso necesita soporte de transmisión, no una sola función de llamada. Este es fácil de entender y recordar, y por lo tanto de mantener.
Maarten Bodewes
59

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 hexes "48656c6c6f20776f726c64".

Stephan202
fuente
En guayaba también puedes usar new HashCode(bytes).toString().
mfulton26
1
A partir de Guava 22.0 esHashCode.fromBytes(checksum).toString()
Devstr
43

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

everconfusedGuy
fuente
56
Este oneliner elimina los primeros cero bytes.
Voicu
@Voicu ... Y agregará un cero inicial el 50% del tiempo.
Maarten Bodewes
27

Aquí hay algunas opciones comunes ordenadas desde simples (una línea) hasta complejas (gran biblioteca). Si está interesado en el rendimiento, vea los micro puntos de referencia a continuación.

Opción 1: fragmento de código: simple

Una solución muy simple es usar la BigIntegerrepresentación hexadecimal de:

new BigInteger(1, someByteArray).toString(16)

Tenga en cuenta que dado que esto maneja números que no son cadenas de bytes arbitrarias , omitirá los ceros a la izquierda; esto puede o no ser lo que desea (por ejemplo, 000AE3vs 0AE3para una entrada de 3 bytes). Esto también es muy lento, aproximadamente 100 veces más lento en comparación con la siguiente opción.

Opción 2: fragmento de código - Avanzado

Aquí hay un fragmento de código completo, copiable y pegable que admite mayúsculas / minúsculas y endianness . Está optimizado para minimizar la complejidad de la memoria y maximizar el rendimiento y debería ser compatible con todas las versiones modernas de Java (5+).

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;

        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}

El código fuente completo con licencia Apache v2 y decodificador se puede encontrar aquí .

Opción 3: Usar una pequeña biblioteca optimizada: bytes-java

Mientras trabajaba en mi proyecto anterior, creé este pequeño kit de herramientas para trabajar con bytes en Java. No tiene dependencias externas y es compatible con Java 7+. Incluye, entre otros, un decodificador / decodificador HEX muy rápido y bien probado:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

Puedes verlo en Github: bytes-java .

Opción 4: Códec de Apache Commons

Por supuesto, están los buenos códecs comunes . ( opinión de advertencia más adelante ) Mientras trabajaba en el proyecto descrito anteriormente, analicé el código y me decepcionó bastante; una gran cantidad de códigos no organizados duplicados, códecs obsoletos y exóticos probablemente solo sean útiles para muy pocas implementaciones muy complejas y lentas de códecs populares (específicamente Base64). Por lo tanto, tomaría una decisión informada si desea utilizarla o una alternativa. De todos modos, si todavía quieres usarlo, aquí hay un fragmento de código:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

Opción 5: Google Guava

La mayoría de las veces ya tienes a la guayaba como dependencia. Si es así, solo use:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

Opción 6: seguridad de primavera

Si usa Spring Framework con Spring Security , puede usar lo siguiente:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

Opción 7: Castillo hinchable

Si ya usa el marco de seguridad Bouncy Castle , puede usar su utilidad Hex:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

No es realmente la opción 8: compatibilidad con Java 9+ o 'No use JAXBs javax / xml / bind / DatatypeConverter'

En versiones anteriores de Java (8 y posteriores), el código Java para JAXB se incluía como dependencia de tiempo de ejecución. Desde Java 9 y la modularización de Jigsaw, su código no puede acceder a otro código fuera de su módulo sin una declaración explícita. Así que tenga en cuenta si obtiene una excepción como:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

cuando se ejecuta en una JVM con Java 9+. Si es así, cambie las implementaciones a cualquiera de las alternativas anteriores. Ver también esta pregunta .


Micro Benchmarks

Estos son los resultados de una simple referencia de micro JMH que codifica conjuntos de bytes de diferentes tamaños . Los valores son operaciones por segundo, por lo que mayor es mejor. Tenga en cuenta que los micro puntos de referencia a menudo no representan el comportamiento del mundo real, por lo tanto, tome estos resultados con un grano de sal.

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

Especificaciones: JDK 8u202, i7-7700K, Win10, 24GB Ram. Vea el punto de referencia completo aquí .

Patrick Favre
fuente
21

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

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Usagi Miyamoto
fuente
2
Gracias, esto es muy conciso y apropiado.
Deepan Prabhu Babu
18

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

El más elegante, como él 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();
}
Michael Bisbjerg
fuente
Otros métodos se estaban ejecutando en mi muestra de 64 bytes en 5 ms, este se ejecuta en 0 ms. Probablemente sea mejor por falta de otras funciones de cadena como el formato.
Joseph Lust
if (raw == null) return nullNo es fallar rápido. ¿Por qué usarías alguna vez una nullllave?
Maarten Bodewes
Supongo que es un hábito ingresar validación. En este caso, evitamos cualquier excepción de referencia nula y dejamos que la persona que llama maneje los datos incorrectos.
Michael Bisbjerg
16

Con el menor costo de almacenar 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<len) {
      hexIndex=(bytes[ofs++] & 0xFF)<<1;
      chars[idx++]=BYTE2HEX[hexIndex++];
      chars[idx++]=BYTE2HEX[hexIndex];
    }
    return new String(chars);
  }
higginse
fuente
66
¿Por qué no inicializar la BYTE2HEXmatriz con un forciclo simple ?
icza
@icza ¿Es eso posible con un campo estático final (también conocido como constante)?
nevelis
1
@nevelis Se puede asignar en un static { }bloque.
マ ル ち ゃ ん だ よ
1
@icza porque es más rápido codificar una tabla de búsqueda que generarla. Aquí la complejidad de la memoria se comercializa con la complejidad del tiempo, es decir. necesita más memoria pero más rápido (cada tanto en ambos extremos)
Patrick Favre
8

¿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;
    }
Manan Bakshi
fuente
3

No necesitamos usar ninguna biblioteca externa o escribir código basado en bucles y constantes.
Es suficiente solo esto:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Mariana
fuente
1
Esto es muy similar a la respuesta de Guy siempre confusa.
Scratte
2

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 ligeramente más flexible de la respuesta aceptada. Personalmente, mantengo la respuesta aceptada y esta sobrecarga junto con ella, utilizable en más contextos.

Bamaco
fuente
La pregunta original era para el byte [] a String. Mire hexadecimal a bytes [] o haga una pregunta diferente, @NonExistent.
Bamaco
2

Usualmente uso el siguiente método para la declaración de 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();
}
Snox
fuente
2
Si su debuffer tiene un mal día, trate cluing en StringBuilder instanciación con un número de caracteres para el apoyo: StringBuilder buf = new StringBuilder(data.length * 2);.
barba gris
2

De acuerdo, hay muchas maneras de hacer esto, pero si decides usar una biblioteca, te sugiero que analices tu proyecto para ver si algo se ha implementado en una biblioteca que ya forma parte de tu proyecto antes de agregar una nueva biblioteca solo para hacer esto. Por ejemplo si aún no tienes

org.apache.commons.codec.binary.Hex

tal vez tienes ...

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

Aaron Cooley
fuente
2

Si está utilizando el marco de Spring Security, puede usar:

import org.springframework.security.crypto.codec.Hex

final String testString = "Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));
java-addict301
fuente
2

Agregar una jarra de utilidad para una función simple no es una buena opción. En su lugar, reúna sus propias clases de utilidad. lo siguiente es posible una implementación más rápida.

public class ByteHex {

    public static int hexToByte(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 String[] byteToHexTable = new String[]
    {
        "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"
    };

    private static final String[] byteToHexTableLowerCase = new String[]
    {
        "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"
    };

    public static String byteToHex(byte b){
        return byteToHexTable[b & 0xFF];
    }

    public static String byteToHex(byte[] bytes){
        if(bytes == null) return null;
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
        return sb.toString();
    }

    public static String byteToHex(short[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
        return sb.toString();
    }

    public static String byteToHexLowerCase(byte[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
        return sb.toString();
    }

    public static byte[] hexToByte(String hexString) {
        if(hexString == null) return null;
        byte[] byteArray = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
        }
        return byteArray;
    }

    public static byte hexPairToByte(char ch1, char ch2) {
        return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
    }


}
Krishna Telgave
fuente
1

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
Campa
fuente
1

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

  1. Usa un bucle
  2. Use javax.xml.bind.DatatypeConverter que compila 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 con 62 códigos de operación, pero si puedes vivir sin relleno 0 en caso de que el primer byte sea menor que 0x10, entonces la siguiente solución solo usa 23 códigos de operación. Realmente muestra cómo las soluciones "fáciles de implementar usted mismo" como "pad con un 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 la 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);
}
Dmitry
fuente
1

Mi solución se basa en la solución maybeWeCouldStealAVan, pero no se basa en ninguna tabla de búsqueda asignada adicionalmente. No utiliza ningún truco de conversión de 'int-to-char' (en realidad, lo Character.forDigit()hace, realizando alguna comparación para verificar cuál es realmente el dígito) y, por lo tanto, podría ser un poco más lento. Por favor, siéntase libre de usarlo donde quiera. Salud.

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);
}
Alambrada
fuente
0

// 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();
}
BluePurse
fuente
0

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=[])
SkippsDev
fuente
0
  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;
  } 
田 咖啡
fuente
0

Aquí hay una java.util.Base64implementación similar (parcial), ¿no es bonita?

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;
    }
    //...
}
fuweichin
fuente
0
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();
    }
kakopappa
fuente
0
Converts bytes data to hex characters

@param bytes byte array to be converted to hex string
@return byte String in hex format

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
Rajneesh Shukla
fuente