Omzetten tussen byte-arrays en hexadecimale strings in Java

1. Overzicht

In deze zelfstudie bekijken we verschillende manieren om een ​​byte-array naar een hexadecimaal te converteren Draad, en vice versa.

We zullen ook het conversiemechanisme begrijpen en onze implementatie schrijven om dit te bereiken.

2. Omzetten tussen byte en hexadecimaal

Laten we eerst eens kijken naar de conversielogica tussen byte en hexadecimale getallen.

2.1. Byte naar hexadecimaal

De bytes zijn 8-bits gehele getallen met teken in Java. Daarom moeten we converteer elk 4-bits segment afzonderlijk naar hexadecimaal en voeg ze samen. Daarom krijgen we na conversie twee hexadecimale tekens.

We kunnen bijvoorbeeld 45 schrijven als 0010 1101 in binair, en het hexadecimale equivalent is "2d":

0010 = 2 (basis 10) = 2 (basis 16) 1101 = 13 (basis 10) = d (basis 16) Daarom: 45 = 0010 1101 = 0x2d 

Laten we deze eenvoudige logica in Java implementeren:

openbare String byteToHex (aantal bytes) {char [] hexDigits = new char [2]; hexDigits [0] = Character.forDigit ((num >> 4) & 0xF, 16); hexDigits [1] = Character.forDigit ((num & 0xF), 16); retourneer nieuwe String (hexDigits); }

Laten we nu de bovenstaande code begrijpen door elke bewerking te analyseren. Allereerst hebben we een char-array van lengte 2 gemaakt om de uitvoer op te slaan:

char [] hexDigits = nieuw char [2];

Vervolgens hebben we bits van hogere orde geïsoleerd door 4 bits naar rechts te verschuiven. En toen hebben we een masker toegepast om 4 bits van lagere orde te isoleren. Maskeren is vereist omdat negatieve getallen intern worden weergegeven als twee-complement van het positieve getal:

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

Vervolgens converteren we de resterende 4 bits naar hexadecimaal:

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

Ten slotte maken we een Draad object uit de char-array. En retourneerde vervolgens dit object als geconverteerde hexadecimale array.

Laten we nu eens kijken hoe dit werkt voor een negatieve byte -4:

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 Daarom: -4 (basis 10) = 1111 1100 (basis 2) = fc (basis 16)

Het is ook vermeldenswaard dat de Karakter.forDigit() methode retourneert altijd kleine letters.

2.2. Hexadecimaal naar byte

Laten we nu een hexadecimaal cijfer naar byte converteren. Zoals we weten, bevat een byte 8 bits. Daarom we hebben twee hexadecimale cijfers nodig om één byte te creëren.

Allereerst zullen we elk hexadecimaal cijfer afzonderlijk naar een binair equivalent converteren.

En dan moeten we de twee vier bitsegmenten aaneenschakelen om het byte-equivalent te krijgen:

Hexadecimaal: 2d 2 = 0010 (basis 2) d = 1101 (basis 2) Daarom: 2d = 0010 1101 (basis 2) = 45

Laten we nu de bewerking in Java schrijven:

openbare 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 ("Ongeldig hexadecimaal teken:" + hexChar); } retour cijfer; }

Laten we dit een operatie per keer begrijpen.

Allereerst hebben we hexadecimale tekens omgezet in gehele getallen:

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

Daarna verlieten we het meest significante cijfer met 4 bits. Bijgevolg heeft de binaire weergave nullen op vier minst significante bits.

Vervolgens hebben we het minst significante cijfer eraan toegevoegd:

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

Laten we nu eens kijken naar het toDigit () methode nauw. We gebruiken de Character.digit () methode voor conversie. Als de tekenwaarde die aan deze methode wordt doorgegeven, geen geldig cijfer is in de opgegeven radix, wordt -1 geretourneerd.

We valideren de geretourneerde waarde en genereren een uitzondering als er een ongeldige waarde is doorgegeven.

3. Omzetten tussen byte-arrays en hexadecimaal Snaren

Op dit punt weten we hoe we een byte moeten converteren naar het hexadecimale getal en vice versa. Laten we dit algoritme schalen en byte-array converteren naar / van hexadecimaal Draad.

3.1. Byte Array naar hexadecimaal Draad

We moeten de array doorlopen en een hexadecimaal paar genereren voor elke byte:

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

Zoals we al weten, wordt de uitvoer altijd in kleine letters weergegeven.

3.2. Hexadecimale tekenreeks naar byte-array

Allereerst moeten we controleren of de lengte van de hexadecimaal Draad is een even getal. Dit komt doordat een hexadecimaal Draad met een oneven lengte zal resulteren in een onjuiste weergave van bytes.

Nu gaan we door de array heen en converteren we elk hexadecimaal paar naar een byte:

openbare byte [] decodeHexString (String hexString) {if (hexString.length ()% 2 == 1) {throw new IllegalArgumentException ("Ongeldige hexadecimale string opgegeven."); } byte [] bytes = nieuwe byte [hexString.length () / 2]; voor (int i = 0; i <hexString.length (); i + = 2) {bytes [i / 2] = hexToByte (hexString.substring (i, i + 2)); } retour bytes; }

4. Gebruik de BigInteger Klasse

Wij kunnen maak een object van het type BigInteger door een signum- en byte-array door te geven.

Nu kunnen we het hexadecimale Draad met behulp van het statische methode-formaat gedefinieerd in Draad klasse:

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

Het opgegeven formaat genereert een hexadecimaal met nul opgevulde kleine letters Draad. We kunnen ook een tekenreeks in hoofdletters genereren door "x" te vervangen door "X".

Als alternatief hadden we de toString () methode van BigInteger. Het subtiele verschil tussen het gebruik van de toString () methode is dat de uitvoer niet wordt opgevuld met voorloopnullen:

openbare String encodeUsingBigIntegerToString (byte [] bytes) {BigInteger bigInteger = nieuwe BigInteger (1, bytes); retourneer bigInteger.toString (16); }

Laten we nu eens kijken naar hexadecimaal Draad naar byte Matrixconversie:

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

De toByteArray () methode produceert een extra tekenbit. We hebben een specifieke code geschreven voor het afhandelen van dit extra bit.

Daarom moeten we van deze details op de hoogte zijn voordat we het BigInteger klasse voor de conversie.

5. Gebruik de DataTypeConverter Klasse

De DataTypeConverter klasse wordt geleverd met JAXB-bibliotheek. Dit maakt tot Java 8 deel uit van de standaardbibliotheek. Vanaf Java 9 moeten we toevoegen java.xml.bind module expliciet naar de runtime.

Laten we de implementatie eens bekijken met behulp van de DataTypeConverter klasse:

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

Zoals hierboven weergegeven, is het erg handig in gebruik DataTypeConverter klasse. De output van de printHexBinary () methode is altijd in hoofdletters. Deze klasse biedt een reeks afdruk- en parse-methoden voor datatype-conversie.

Voordat we voor deze aanpak kiezen, moeten we ervoor zorgen dat de klasse beschikbaar is tijdens runtime.

6. Gebruik van Apache's Commons-Codec Library

We kunnen de Hex klasse geleverd met de Apache commons-codec-bibliotheek:

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

De output van encodeHexString staat altijd in kleine letters.

7. Met behulp van de Guava-bibliotheek van Google

Laten we eens kijken hoe BaseEncoding class kan worden gebruikt voor het coderen en decoderen van byte-array naar het hexadecimale Draad:

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

De BaseEncoding codeert en decodeert standaard met hoofdletters. Als we kleine letters moeten gebruiken, moet een nieuwe coderingsinstantie worden gemaakt met behulp van de statische methode in kleine letters.

8. Conclusie

In dit artikel hebben we het conversie-algoritme tussen byte-array naar hexadecimaal geleerd Draad. We hebben ook verschillende methoden besproken om byte-array naar hex-string te coderen en vice versa.

Het wordt niet aangeraden om een ​​bibliotheek toe te voegen om alleen een paar hulpprogramma-methoden te gebruiken. Daarom, als we de externe bibliotheken niet al gebruiken, moeten we het besproken algoritme gebruiken. De DataTypeConverter class is een andere manier om te coderen / decoderen tussen verschillende gegevenstypen.

Ten slotte is de volledige broncode van deze tutorial beschikbaar op GitHub.