Konvertieren zwischen Byte-Arrays und Hexadezimal-Zeichenfolgen in Java

Konvertieren zwischen Byte-Arrays und hexadezimalen Zeichenfolgen in Java

 

1. Überblick

In diesem Tutorial werden verschiedene Möglichkeiten zum Konvertieren eines Byte-Arrays in hexadezimaleString, und umgekehrt beschrieben.

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 bis Hexadezimal

Die Bytes sind in Java 8-Bit-Ganzzahlen mit Vorzeichen. Daher müssen wirconvert each 4-bit segment to hex separately and concatenate them. Folglich erhalten wir nach der Konvertierung zwei hexadezimale Zeichen.

Zum Beispiel können wir 45 als 0010 1101 im Binärformat 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

Implementieren wir diese einfache Logik in Java:

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 der Länge 2 erstellt, um die Ausgabe zu speichern:

char[] hexDigits = new char[2];

Als nächstes isolierten wir höherwertige Bits, indem wir 4 Bits nach rechts schoben. 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 einString-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 dieCharacter.forDigit _ () _ -Methode immer Kleinbuchstaben zurückgibt.

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 istwe need two hexadecimal digits to create one byte.

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 ganze Zahlen umgewandelt:

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

Dann haben wir die höchstwertige Stelle um 4 Bits nach links verschoben. Folglich hat die Binärdarstellung Nullen bei vier niedrigstwertigen Bits.

Dann haben wir die niedrigstwertige Ziffer hinzugefügt:

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

Lassen Sie uns nun dietoDigit()-Methode genau untersuchen. Wir verwenden dieCharacter.digit()-Methode für die Konvertierung. If the character value passed to this method is not a valid digit in the specified radix, -1 is returned.

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 hexadezimalemStrings

Zu diesem Zeitpunkt wissen wir, wie ein Byte in das Hexadezimalformat konvertiert wird und umgekehrt. Skalieren wir diesen Algorithmus und konvertieren das Byte-Array in / von hexadezimalenString.

3.1. Byte-Array zu hexadezimalString

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();
}

Wie wir bereits wissen, wird die Ausgabe immer in Kleinbuchstaben erfolgen.

3.2. Hexadezimaler String zum Byte-Array

Zunächst müssen wir prüfen, ob die Länge der hexadezimalenString eine gerade Zahl ist. Dies liegt daran, dass ein hexadezimalesString mit ungerader Länge zu einer falschen Bytedarstellung führt.

Jetzt durchlaufen wir das Array und konvertieren jedes hexadezimale Paar in ein 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. Verwenden derBigInteger-Klasse

Wir könnencreate an object of type BigInteger by passing a signum and byte array.

Jetzt können wir die hexadezimalenString mit Hilfe des statischen Methodenformats generieren, das in der KlasseStringdefiniert ist:

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

Das bereitgestellte Format generiert ein mit Nullen aufgefülltes hexadezimales KleinbuchstabenString.. Wir können auch eine Zeichenfolge in Großbuchstaben generieren, indem wir "x" durch "X" ersetzen.

Alternativ hätten wir dietoString()-Methode vonBigInteger verwenden können. Die subtilendifference 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);
}

Schauen wir uns nun die hexadezimale Array-Konvertierung vonString inbytean:

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. Wir haben speziellen Code für die Behandlung dieses zusätzlichen Bits geschrieben.

Daher sollten wir diese Details kennen, bevor wir die KlasseBigIntegerfür die Konvertierung verwenden.

5. Verwenden derDataTypeConverter-Klasse

Die KlasseDataTypeConverterwird mit der JAXB-Bibliothek geliefert. Dies ist Teil der Standardbibliothek bis Java 8. Ab Java 9 müssen wir der Laufzeit explizit das Moduljava.xml.bindhinzufügen.

Werfen wir einen Blick auf die Implementierung mit der KlasseDataTypeConverter:

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

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

Wie oben gezeigt, ist es sehr praktisch, die KlasseDataTypeConverterzu verwenden. Dieoutput of the printHexBinary() method is always in uppercase. Diese Klasse stellt eine Reihe von Druck- und Analysemethoden für die Datentypkonvertierung bereit.

Bevor wir diesen Ansatz wählen, müssen wir sicherstellen, dass die Klasse zur Laufzeit verfügbar ist.

6. Verwenden der Commons-Codec-Bibliothek von Apache

Wir können die KlasseHexverwenden, die mit der Apache Commons-Codec-Bibliothek geliefert wird:

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

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

Dieoutput of encodeHexString is always in lowercase.

7. Verwenden der Guavenbibliothek von Google

Schauen wir uns an, wie die KlasseBaseEncodingzum Codieren und Decodieren des Byte-Arrays in das hexadezimaleString: verwendet werden kann

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. Wenn wir Kleinbuchstaben verwenden müssen, sollte eine neue Codierungsinstanz mit der statischen Methodelowercase erstellt werden.

8. Fazit

In diesem Artikel haben wir den Konvertierungsalgorithmus zwischen Bytearray und hexadezimalemString gelernt. Wir haben auch verschiedene Methoden zum Codieren von Byte-Arrays in Hex-Strings und umgekehrt besprochen.

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 KlasseDataTypeConverter ist eine weitere Möglichkeit zum Codieren / Decodieren zwischen verschiedenen Datentypen.

Schließlich lautet der vollständige Quellcode dieses Tutorialsavailable on GitHub.