Mostrando entradas con la etiqueta conversion. Mostrar todas las entradas
Mostrando entradas con la etiqueta conversion. Mostrar todas las entradas

2014-09-03

Convertir String Hexadecimal en array de bytes en Java

Os presento el método inverso al de Convertir array de bytes en String de los valores en Hexadecimal

El método más optimizado que he encontrado en Java es el siguiente:
byte[] javax.xml.bind.DatatypeConverter.parseHexBinary(String lexicalXSDHexBinary)
Para mostrar su eficiencia voy a compararlo con este otro método [stackoverflow.com] bastante bien valorado por los usuarios:
    private static byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }
Y para ello, aprovecho para mostrar como se puede hacer un pequeño benchmark comparativo:
import javax.xml.bind.DatatypeConverter;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * @author Raul
 */
public final class Benchmark {

    private static byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    private static final char[] VALORES = "0123456789ABCDEF".toCharArray();
    private static final int MIN = 0;
    private static final int MAX = 15;

    private static final int NUM_TESTS = 1000000;
    private static final int LENGTH_HEX = 64;

    private static final long TIME_ACCURACY = 1000000;

    public static void main(String[] args) {
        // Preparamos los tests
        Random random = new Random();
        int range = MAX - MIN + 1;
        List<String> tests = new LinkedList<String>();
        for (int i = 0; i < NUM_TESTS; i++) {
            StringBuilder sb = new StringBuilder(LENGTH_HEX);
            for (int b = 0; b < LENGTH_HEX; b++) {
                int randomPos = random.nextInt(range) + MIN;
                sb.append(VALORES[randomPos]);
            }
            tests.add(sb.toString());
        }

        // Iniciamos
        long startTime = 0, endTime = 0;
        System.gc();

        // DatatypeConverter
        startTime = System.nanoTime();
        for (String test : tests) {
            byte[] result = DatatypeConverter.parseHexBinary(test);
        }
        endTime = System.nanoTime();
        System.out.printf("%22s %10d%n", "DatatypeConverter:",
                (endTime - startTime) / TIME_ACCURACY);
        System.gc();

        // hexStringToByteArray
        startTime = System.nanoTime();
        for (String test : tests) {
            byte[] result = hexStringToByteArray(test);
        }
        endTime = System.nanoTime();
        System.out.printf("%22s %10d%n", "hexStringToByteArray:",
                (endTime - startTime) / TIME_ACCURACY);
        System.gc();
    }

}
Resultado:
    DatatypeConverter:        663
 hexStringToByteArray:        908

El paquete de DatatypeConverter no está disponible en Android, así que los que necesitéis este método podéis utilizar el hexStringToByteArray expuesto aquí, o el código del parseHexBinary extraído del propio paquete y que podéis encontrar en http://stackoverflow.com/a/11139098

2014-08-26

Convertir array de bytes en String de los valores en Hexadecimal

Cuando trabajaba en el algoritmo para crear un MAC ANSI X9.19 tuve que convertir el resultado que estaba en un array de bytes (byte[] result) en un String que representase cada byte como un valor Hexadecimal. Para ello, utilicé una función que encontré en stackoverflow y que resultaba elegante a simple vista:
    private static String toHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b & 0xff)); // "%02X" for uppercase
        }
        return sb.toString();
    }

El caso es que mientras buscaba el código para el MAC ANSI X9.19 me encontré muchas formas de implementar esta conversión y he tenido la curiosidad de indagar cuál es la más eficiente.

Quiero comentar que hay librerías de uso bastante común que incorporan esta conversión, por ejemplo:
  • org.apache.commons.codec.binary.Hex.encodeHexString() de la librería Apache Commons Codec
  • org.bouncycastle.util.encoders.Hex.encode() de la librería Bouncy Castle
Pero es más, aunque mucha gente no lo sabe y acaba recurriendo a librerías externas, viene también incluida en la propia distribución de Java: javax.xml.bind.DatatypeConverter.printHexBinary()

Pues bien, ninguna de las implementaciones comentadas resulta ser demasiado eficiente. Así que si necesitáis una conversión que sea rápida, no queréis incluir librerías externas o estáis programando en Android y necesitáis esta conversión porque se dispone del DatatypeConverter en Android, aquí va el método óptimo de conversión. Mediante un benchmark personalizado que construí he podido comprobar que es el doble de rápido que el mejor de los demás que he probado y 100 veces más rápido que las conversiones incluidas en librerías comentadas o la propia DatatypeConverter.printHexBinary()

    private static final char[] hexArray = "0123456789ABCDEF".toCharArray();

    /**
     * Encode the input bytes producing a Hexadecimal output string
     *
     * @param bytes
     * @return Hexadecimal representation of the input bytes
     */
    public static final String byteArrayToHexString(final 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);
    }

Todo el mérito y mi reconocimiento es para esta contribución en stackoverflow: http://stackoverflow.com/a/9855338