Articles

Conversão Entre Arrays de Byte e Cordas Hexadecimais em Java

Visão Geral

Neste tutorial, vamos dar uma vista de olhos a diferentes formas de converter uma matriz de bytes para uma corda hexadecimal, e vice-versa.

Tambem vamos compreender o mecanismo de conversão e escrever a nossa implementação para o conseguir.

Conversão entre Byte e Hexadecimal

P>Primeiro de tudo, vamos dar uma vista de olhos à lógica de conversão entre byte e números hexadecimais.

2.1. Byte para Hexadecimal

Os bytes são inteiros de 8 bit assinados em Java. Portanto, precisamos de converter cada segmento de 4 bits para hexadecimal separadamente e concatená-los. Consequentemente, iremos obter dois caracteres hexadecimais após a conversão.

Por exemplo, podemos escrever 45 como 0010 1101 em binário, e o equivalente hexadecimal será “2d”:

0010 = 2 (base 10) = 2 (base 16)1101 = 13 (base 10) = d (base 16)Therefore: 45 = 0010 1101 = 0x2d

P>Deixamos implementar esta lógica simples em Java:

public String byteToHex(byte num) { char hexDigits = new char; hexDigits = Character.forDigit((num >> 4) & 0xF, 16); hexDigits = Character.forDigit((num & 0xF), 16); return new String(hexDigits);}

Agora, vamos compreender o código acima analisando cada operação. Antes de mais, criámos uma matriz de caracteres de comprimento 2 para armazenar a saída:

char hexDigits = new char;

Next, isolámos bits de ordem superior deslocando 4 bits para a direita. E depois, aplicámos uma máscara para isolar os bits de ordem inferior 4 bits. A máscara é necessária porque os números negativos são representados internamente como complemento de dois do número positivo:

hexDigits = Character.forDigit((num >> 4) & 0xF, 16);

Então convertemos os 4 bits restantes para hexadecimal:

hexDigits = Character.forDigit((num & 0xF), 16);

Finalmente, criamos um objecto String a partir da matriz de caracteres. E depois, devolvemos este objecto como um array hexadecimal convertido.

Agora, vamos entender como isto funcionará para um byte negativo -4:

hexDigits:1111 1100 >> 4 = 1111 1111 1111 1111 1111 1111 1111 11111111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 = 0xfhexDigits:1111 1100 & 0xF = 0000 1100 = 0xcTherefore: -4 (base 10) = 1111 1100 (base 2) = fc (base 16)

É também de notar que o método Character.forDigit() devolve sempre caracteres minúsculos.

2.2. Hexadecimal para Byte

Agora, vamos converter um dígito hexadecimal para byte. Como sabemos, um byte contém 8 bits. Portanto, precisamos de dois dígitos hexadecimais para criar um byte.

P>Primeiro de tudo, vamos converter cada dígito hexadecimal em equivalente binário separadamente.

E depois, precisamos de concatenar os dois quatro segmentos de bits para obter o equivalente do byte:

Hexadecimal: 2d2 = 0010 (base 2)d = 1101 (base 2)Therefore: 2d = 0010 1101 (base 2) = 45

Agora, vamos escrever a operação em Java:

public byte hexToByte(String hexString) { int firstDigit = toDigit(hexString.charAt(0)); int secondDigit = toDigit(hexString.charAt(1)); return (byte) ((firstDigit << 4) + secondDigit);}private int toDigit(char hexChar) { int digit = Character.digit(hexChar, 16); if(digit == -1) { throw new IllegalArgumentException( "Invalid Hexadecimal Character: "+ hexChar); } return digit;}

Vamos compreender isto, uma operação de cada vez.

P>Primeiro de tudo, convertemos caracteres hexadecimais em inteiros:

int firstDigit = toDigit(hexString.charAt(0));int secondDigit = toDigit(hexString.charAt(1));

Então deixámos deslocar o dígito mais significativo por 4 bits. Consequentemente, a representação binária tem zeros de pelo menos quatro bits significativos.

Então, adicionámos-lhe o dígito menos significativo:

return (byte) ((firstDigit << 4) + secondDigit);

Agora, vamos examinar de perto o método toDigit(). Estamos a utilizar o método Character.digit() para a conversão. Se o valor de caractere passado para este método não for um dígito válido no radix especificado, -1 é devolvido.

Estamos a validar o valor de retorno e a lançar uma excepção se um valor inválido for passado.

Convertendo entre Arrays de Byte e Cordas Hexadecimais

Neste ponto, sabemos como converter um byte para o hexadecimal, e vice-versa. Vamos escalar este algoritmo e converter uma matriz de bytes para/de Cordas hexadecimais.

3.1. Byte Array para Hexadecimal String

Precisamos de fazer loop através do array e gerar par hexadecimal para cada byte:

public String encodeHexString(byte byteArray) { StringBuffer hexStringBuffer = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { hexStringBuffer.append(byteToHex(byteArray)); } return hexStringBuffer.toString();}

Como já sabemos, a saída será sempre em minúsculas.

3.2. Hexadecimal String to Byte Array

P>Primeiro de tudo, precisamos de verificar se o comprimento da corda hexadecimal é um número par. Isto porque uma corda hexadecimal com comprimento ímpar resultará numa representação incorrecta de bytes.

Agora, vamos iterar através da matriz e converter cada par hexadecimal num byte:

public byte decodeHexString(String hexString) { if (hexString.length() % 2 == 1) { throw new IllegalArgumentException( "Invalid hexadecimal String supplied."); } byte bytes = new byte; for (int i = 0; i < hexString.length(); i += 2) { bytes = hexToByte(hexString.substring(i, i + 2)); } return bytes;}

Usando a Classe BigInteger

Podemos criar um objecto do tipo BigInteger passando uma matriz de signum e bytes.

Agora, podemos gerar a String hexadecimal com a ajuda do formato do método estático definido na classe String:

public String encodeUsingBigIntegerStringFormat(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return String.format( "%0" + (bytes.length << 1) + "x", bigInteger);}

O formato fornecido irá gerar uma String hexadecimal em letra minúscula e sem quadrícula. Também podemos gerar uma string em maiúsculas substituindo “x” por “X”.

Alternativamente, poderíamos ter usado o método toString() da BigInteger. A diferença subtil de usar o método toString() é que a saída não é acolchoada com zeros:

public String encodeUsingBigIntegerToString(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return bigInteger.toString(16);}

Agora, vamos dar uma olhada à conversão hexadecimal String to byte Array:

public byte decodeUsingBigInteger(String hexString) { byte byteArray = new BigInteger(hexString, 16) .toByteArray(); if (byteArray == 0) { byte output = new byte; System.arraycopy( byteArray, 1, output, 0, output.length); return output; } return byteArray;}

O método toByteArray() produz um bit de sinal adicional. Escrevemos um código específico para lidar com este bit adicional.

Hence, devemos estar atentos a estes detalhes antes de usar a classe BigInteger para a conversão.

Usando a classe DataTypeConverter

A classe DataTypeConverter é fornecida com a biblioteca JAXB. Esta é parte da biblioteca padrão até Java 8. A partir de Java 9, precisamos de adicionar o módulo java.xml.bind ao tempo de execução explicitamente.

p>Vamos dar uma olhada na implementação usando a classe DataTypeConverter:

public String encodeUsingDataTypeConverter(byte bytes) { return DatatypeConverter.printHexBinary(bytes);}public byte decodeUsingDataTypeConverter(String hexString) { return DatatypeConverter.parseHexBinary(hexString);}

Como mostrado acima, é muito conveniente usar a classe DataTypeConverter. A saída do método printHexBinary() está sempre em maiúsculas. Esta classe fornece um conjunto de métodos de impressão e análise para a conversão do tipo de dados.

Antes de escolher esta abordagem, temos de nos certificar de que a classe estará disponível em tempo de execução.

Usando a Biblioteca Apache Commons-Codec

Podemos usar a classe Hex fornecida com a biblioteca Apache commons-codec:

public String encodeUsingApacheCommons(byte bytes) throws DecoderException { return Hex.encodeHexString(bytes);}public byte decodeUsingApacheCommons(String hexString) throws DecoderException { return Hex.decodeHex(hexString);}

A saída do codificadorHexString está sempre em minúsculas.

Usando a Biblioteca da Goiaba do Google

Vejamos como a classe BaseEncoding pode ser usada para codificar e descodificar a matriz de bytes para a String hexadecimal:

public String encodeUsingGuava(byte bytes) { return BaseEncoding.base16().encode(bytes);}public byte decodeUsingGuava(String hexString) { return BaseEncoding.base16() .decode(hexString.toUpperCase());}

A classe BaseEncoding codifica e descodifica usando caracteres maiúsculos por defeito. Se precisarmos de utilizar caracteres minúsculos, deve ser criada uma nova instância de codificação utilizando o método estático minúsculo.

Conclusion

Neste artigo, aprendemos o algoritmo de conversão entre a matriz de bytes para String hexadecimal. Também discutimos vários métodos de codificação de matriz de bytes para string hexadecimal e vice-versa.

Não é aconselhável adicionar uma biblioteca para usar apenas alguns métodos utilitários. Portanto, se não estamos já a utilizar as bibliotecas externas, devemos utilizar o algoritmo discutido. A classe DataTypeConverter é outra forma de codificar/decodificar entre vários tipos de dados.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *