Konvertieren zwischen Byte-Arrays und hexadezimalen Zeichenfolgen in Java

1. Übersicht

In diesem Tutorial sehen wir uns verschiedene Möglichkeiten an, wie ein Byte-Array in einen hexadezimalen String konvertiert werden kann und umgekehrt.

Wir werden auch den Konvertierungsmechanismus verstehen und unsere Implementierung schreiben, um dies zu erreichen.

2. Konvertieren zwischen Byte und Hexadezimal

Schauen wir uns zunächst die Konvertierungslogik zwischen Byte- und Hexadezimalzahlen an.

2.1. Byte nach Hexadezimal

Die Bytes sind in Java 8-Bit-Ganzzahlen mit Vorzeichen. Daher müssen wir jedes 4-Bit-Segment separat in hexadezimal konvertieren und verketten . Folglich erhalten wir nach der Konvertierung zwei hexadezimale Zeichen.

Zum Beispiel können wir 45 als 0010 1101 in Binärform schreiben, und das hexadezimale Äquivalent ist "2d":

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

Lassen Sie uns diese einfache Logik in Java implementieren:

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

Lassen Sie uns nun den obigen Code verstehen, indem wir jede Operation analysieren. Zunächst haben wir ein char-Array mit der Länge 2 erstellt, um die Ausgabe zu speichern:

char[] hexDigits = new char[2];

Als nächstes isolierten wir Bits höherer Ordnung, indem wir 4 Bits nach rechts verschoben. Und dann haben wir eine Maske angewendet, um 4 Bits niedrigerer Ordnung zu isolieren. Eine Maskierung ist erforderlich, da negative Zahlen intern als Zweierkomplement der positiven Zahl dargestellt werden:

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

Dann konvertieren wir die restlichen 4 Bits in hexadezimal:

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

Schließlich erstellen wir ein String- Objekt aus dem char-Array. Anschließend wurde dieses Objekt als konvertiertes hexadezimales Array zurückgegeben.

Lassen Sie uns nun verstehen, wie dies für ein negatives Byte -4 funktioniert:

hexDigits[0]: 1111 1100 >> 4 = 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 = 0xf hexDigits[1]: 1111 1100 & 0xF = 0000 1100 = 0xc Therefore: -4 (base 10) = 1111 1100 (base 2) = fc (base 16)

Es ist auch erwähnenswert, dass der Charakter. Die forDigit () -Methode gibt immer Kleinbuchstaben zurück.

2.2. Hexadezimal zu Byte

Lassen Sie uns nun eine hexadezimale Ziffer in ein Byte konvertieren. Wie wir wissen, enthält ein Byte 8 Bits. Daher benötigen wir zwei hexadezimale Ziffern, um ein Byte zu erstellen .

Zunächst konvertieren wir jede hexadezimale Ziffer separat in ein binäres Äquivalent.

Und dann müssen wir die beiden vier Bitsegmente verketten, um das Byte-Äquivalent zu erhalten:

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

Schreiben wir nun die Operation in 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; }

Lassen Sie uns dies verstehen, eine Operation nach der anderen.

Zunächst haben wir hexadezimale Zeichen in Ganzzahlen konvertiert:

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

Dann haben wir die höchstwertige Ziffer um 4 Bit verschoben. Folglich hat die binäre Darstellung Nullen bei vier niedrigstwertigen Bits.

Dann haben wir die niedrigstwertige Ziffer hinzugefügt:

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

Lassen Sie uns nun die toDigit () -Methode genau untersuchen. Wir verwenden die Character.digit () -Methode für die Konvertierung. Wenn der an diese Methode übergebene Zeichenwert keine gültige Ziffer im angegebenen Radix ist, wird -1 zurückgegeben.

Wir überprüfen den Rückgabewert und lösen eine Ausnahme aus, wenn ein ungültiger Wert übergeben wurde.

3. Konvertieren zwischen Byte-Arrays und hexadezimalen Zeichenfolgen

An diesem Punkt wissen wir, wie ein Byte in das Hexadezimal konvertiert wird und umgekehrt. Skalieren wir diesen Algorithmus und konvertieren das Byte-Array in / aus einem hexadezimalen String .

3.1. Byte-Array zu hexadezimaler Zeichenfolge

Wir müssen das Array durchlaufen und für jedes Byte ein hexadezimales Paar generieren:

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

As we already know, the output will always be in lowercase.

3.2. Hexadecimal String to Byte Array

First of all, we need to check if the length of the hexadecimal String is an even number. This is because a hexadecimal String with odd length will result in incorrect byte representation.

Now, we'll iterate through the array and convert each hexadecimal pair to a byte:

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

4. Using the BigInteger Class

We can create an object of type BigInteger by passing a signum and byte array.

Now, we can generate the hexadecimal String with the help of static method format defined in String class:

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

The format provided will generate a zero-padded lowercase hexadecimal String. We can also generate an uppercase string by replacing “x” with “X”.

Alternatively, we could've used the toString() method from BigInteger. The subtle difference of using the toString() method is that the output isn't padded with leading zeros:

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

Now, let's take a look at hexadecimal String to byte Array conversion:

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

The toByteArray() method produces an additional sign bit. We have written specific code for handling this additional bit.

Hence, we should be aware of these details before using the BigInteger class for the conversion.

5. Using the DataTypeConverter Class

The DataTypeConverter class is supplied with JAXB library. This is part of the standard library until Java 8. Starting from Java 9, we need to add java.xml.bind module to the runtime explicitly.

Let's take a look at implementation using the DataTypeConverter class:

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

As displayed above, it is very convenient to use DataTypeConverter class. The output of the printHexBinary() method is always in uppercase. This class supplies a set of print and parse methods for data type conversion.

Before choosing this approach, we need to make sure the class will be available at runtime.

6. Using Apache's Commons-Codec Library

We can use the Hex class supplied with the Apache commons-codec library:

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

The output of encodeHexString is always in lowercase.

7. Using Google's Guava Library

Let's take a look at how BaseEncoding class can be used for encoding and decoding byte array to the hexadecimal String:

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

The BaseEncoding encodes and decodes using uppercase characters by default. If we need to use lowercase characters, a new encoding instance should be created using static method lowercase.

8. Conclusion

In diesem Artikel haben wir den Konvertierungsalgorithmus zwischen Bytearray und hexadezimalem String kennengelernt . Wir haben auch verschiedene Methoden zum Codieren von Byte-Arrays in Hex-Strings und umgekehrt diskutiert.

Es wird nicht empfohlen, eine Bibliothek hinzuzufügen, um nur einige Dienstprogrammmethoden zu verwenden. Wenn wir die externen Bibliotheken nicht bereits verwenden, sollten wir daher den beschriebenen Algorithmus verwenden. Die DataTypeConverter- Klasse ist eine weitere Möglichkeit zum Codieren / Decodieren zwischen verschiedenen Datentypen.

Schließlich ist der vollständige Quellcode dieses Tutorials auf GitHub verfügbar.