Conversion entre les tableaux d’octets et les chaînes hexadécimales en Java
Aperçu
Dans ce tutoriel, nous allons jeter un coup d’œil aux différentes façons de convertir un tableau d’octets en une chaîne hexadécimale, et vice versa.
Nous comprendrons également le mécanisme de conversion et écrirons notre implémentation pour y parvenir.
Conversion entre l’octet et l’hexadécimal
D’abord, jetons un coup d’œil à la logique de conversion entre les nombres octets et hexadécimaux.
2.1. Octet vers hexadécimal
Les octets sont des entiers signés de 8 bits en Java. Par conséquent, nous devons convertir chaque segment de 4 bits en hexadécimal séparément et les concaténer. Par conséquent, nous obtiendrons deux caractères hexadécimaux après la conversion.
Par exemple, nous pouvons écrire 45 sous la forme 0010 1101 en binaire, et l’équivalent hexadécimal sera « 2d »:
0010 = 2 (base 10) = 2 (base 16)1101 = 13 (base 10) = d (base 16)Therefore: 45 = 0010 1101 = 0x2d
Mettons en œuvre cette logique simple en 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);}
Maintenant, comprenons le code ci-dessus en analysant chaque opération. Tout d’abord, nous avons créé un tableau char de longueur 2 pour stocker la sortie:
char hexDigits = new char;
Puis, nous avons isolé les bits d’ordre supérieur en décalant de 4 bits vers la droite. Et ensuite, nous avons appliqué un masque pour isoler les 4 bits d’ordre inférieur. Le masquage est nécessaire car les nombres négatifs sont représentés en interne comme complément à deux du nombre positif:
hexDigits = Character.forDigit((num >> 4) & 0xF, 16);
Puis, nous avons converti les 4 bits restants en hexadécimal:
hexDigits = Character.forDigit((num & 0xF), 16);
Enfin, nous avons créé un objet String à partir du tableau de chars. Et ensuite, renvoyé cet objet comme tableau hexadécimal converti.
Maintenant, comprenons comment cela va fonctionner pour un octet négatif -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)
Il est également intéressant de noter que la méthode Character.forDigit() renvoie toujours des caractères minuscules.
2.2. De l’hexadécimal à l’octet
Maintenant, convertissons un chiffre hexadécimal en octet. Comme nous le savons, un octet contient 8 bits. Par conséquent, nous avons besoin de deux chiffres hexadécimaux pour créer un octet.
D’abord, nous allons convertir chaque chiffre hexadécimal en équivalent binaire séparément.
Et ensuite, nous devons concaténer les deux segments de quatre bits pour obtenir l’équivalent octet :
Hexadecimal: 2d2 = 0010 (base 2)d = 1101 (base 2)Therefore: 2d = 0010 1101 (base 2) = 45
Maintenant, écrivons l’opération en 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;}
Comprenons cela, une opération à la fois.
Tout d’abord, nous avons converti les caractères hexadécimaux en nombres entiers :
int firstDigit = toDigit(hexString.charAt(0));int secondDigit = toDigit(hexString.charAt(1));
Puis nous avons décalé à gauche le chiffre le plus significatif de 4 bits. Par conséquent, la représentation binaire comporte des zéros sur les quatre bits les moins significatifs.
Puis, nous lui avons ajouté le chiffre le moins significatif :
return (byte) ((firstDigit << 4) + secondDigit);
Maintenant, examinons de près la méthode toDigit(). Nous utilisons la méthode Character.digit() pour la conversion. Si la valeur du caractère passée à cette méthode n’est pas un chiffre valide dans le radix spécifié, -1 est renvoyé.
Nous validons la valeur de retour et lançons une exception si une valeur invalide a été passée.
Conversion entre les tableaux d’octets et les chaînes hexadécimales
À ce stade, nous savons comment convertir un octet en hexadécimal, et vice versa. Mettons cet algorithme à l’échelle et convertissons un tableau d’octets en/de la chaîne hexadécimale.
3.1. Tableau d’octets vers Chaîne hexadécimale
Nous devons boucler le tableau et générer une paire hexadécimale pour chaque octet :
public String encodeHexString(byte byteArray) { StringBuffer hexStringBuffer = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { hexStringBuffer.append(byteToHex(byteArray)); } return hexStringBuffer.toString();}
Comme nous le savons déjà, la sortie sera toujours en minuscules.
3.2. Chaîne hexadécimale vers tableau d’octets
Tout d’abord, nous devons vérifier si la longueur de la chaîne hexadécimale est un nombre pair. En effet, une Chaîne hexadécimale de longueur impaire entraînera une représentation incorrecte des octets.
Maintenant, nous allons itérer à travers le tableau et convertir chaque paire hexadécimale en un octet:
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;}
Utilisation de la classe BigInteger
Nous pouvons créer un objet de type BigInteger en passant un signe et un tableau d’octets.
Maintenant, nous pouvons générer la chaîne hexadécimale à l’aide de la méthode statique format définie dans la classe String :
public String encodeUsingBigIntegerStringFormat(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return String.format( "%0" + (bytes.length << 1) + "x", bigInteger);}
Le format fourni générera une chaîne hexadécimale minuscule à remplissage nul. Nous pouvons également générer une chaîne de caractères en majuscules en remplaçant « x » par « X ».
Au lieu de cela, nous aurions pu utiliser la méthode toString() de BigInteger. La différence subtile de l’utilisation de la méthode toString() est que la sortie n’est pas rembourrée avec des zéros de tête:
public String encodeUsingBigIntegerToString(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return bigInteger.toString(16);}
Maintenant, regardons la conversion d’une chaîne hexadécimale en 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;}
La méthode toByteArray() produit un bit de signe supplémentaire. Nous avons écrit un code spécifique pour gérer ce bit supplémentaire.
Il faut donc connaître ces détails avant d’utiliser la classe BigInteger pour la conversion.
Utilisation de la classe DataTypeConverter
La classe DataTypeConverter est fournie avec la bibliothèque JAXB. Elle fait partie de la bibliothèque standard jusqu’à Java 8. À partir de Java 9, nous devons ajouter explicitement le module java.xml.bind au runtime.
Regardons la mise en œuvre en utilisant la classe DataTypeConverter:
public String encodeUsingDataTypeConverter(byte bytes) { return DatatypeConverter.printHexBinary(bytes);}public byte decodeUsingDataTypeConverter(String hexString) { return DatatypeConverter.parseHexBinary(hexString);}
Comme affiché ci-dessus, il est très pratique d’utiliser la classe DataTypeConverter. La sortie de la méthode printHexBinary() est toujours en majuscules. Cette classe fournit un ensemble de méthodes print et parse pour la conversion des types de données.
Avant de choisir cette approche, nous devons nous assurer que la classe sera disponible au moment de l’exécution.
Utilisation de la bibliothèque Commons-Codec d’Apache
Nous pouvons utiliser la classe Hex fournie avec la bibliothèque Commons-Codec d’Apache :
public String encodeUsingApacheCommons(byte bytes) throws DecoderException { return Hex.encodeHexString(bytes);}public byte decodeUsingApacheCommons(String hexString) throws DecoderException { return Hex.decodeHex(hexString);}
La sortie de encodeHexString est toujours en minuscules.
Utilisation de la bibliothèque Guava de Google
Voyons comment la classe BaseEncoding peut être utilisée pour encoder et décoder un tableau d’octets vers la chaîne hexadécimale:
public String encodeUsingGuava(byte bytes) { return BaseEncoding.base16().encode(bytes);}public byte decodeUsingGuava(String hexString) { return BaseEncoding.base16() .decode(hexString.toUpperCase());}
La classe BaseEncoding encode et décode en utilisant des caractères majuscules par défaut. Si nous devons utiliser des caractères minuscules, il faut créer une nouvelle instance d’encodage en utilisant la méthode statique lowercase.
Conclusion
Dans cet article, nous avons appris l’algorithme de conversion entre le tableau d’octets en String hexadécimal. Nous avons également discuté de diverses méthodes pour coder un tableau d’octets en chaîne hexadécimale et vice versa.
Il n’est pas conseillé d’ajouter une bibliothèque pour utiliser quelques méthodes utilitaires seulement. Par conséquent, si nous n’utilisons pas déjà les bibliothèques externes, nous devrions utiliser l’algorithme discuté. La classe DataTypeConverter est un autre moyen de coder/décoder entre divers types de données.