Mengubah Antara Byte Array dan String Heksadesimal di Java

1. Ikhtisar

Dalam tutorial ini, kita akan melihat berbagai cara untuk mengubah array byte menjadi String heksadesimal , dan sebaliknya.

Kami juga akan memahami mekanisme konversi dan menulis implementasi kami untuk mencapai ini.

2. Mengubah Antara Byte dan Heksadesimal

Pertama-tama, mari kita lihat logika konversi antara bilangan byte dan heksadesimal.

2.1. Byte ke Heksadesimal

Byte adalah bilangan bulat bertanda 8 bit di Java. Oleh karena itu, kita perlu mengonversi setiap segmen 4-bit menjadi hex secara terpisah dan menggabungkannya . Akibatnya, kita akan mendapatkan dua karakter heksadesimal setelah konversi.

Misalnya, kita dapat menulis 45 sebagai 0010 1101 dalam biner, dan padanan heksadesimalnya akan menjadi "2d":

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

Mari menerapkan logika sederhana ini di 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); }

Sekarang, mari kita pahami kode di atas dengan menganalisis setiap operasi. Pertama-tama, kami membuat array karakter dengan panjang 2 untuk menyimpan hasilnya:

char[] hexDigits = new char[2];

Selanjutnya, kami mengisolasi bit orde tinggi dengan menggeser 4 bit ke kanan. Dan kemudian, kami menerapkan topeng untuk mengisolasi urutan bawah 4 bit. Masking diperlukan karena bilangan negatif secara internal direpresentasikan sebagai komplemen dua dari bilangan positif:

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

Kemudian kami mengubah 4 bit yang tersisa menjadi heksadesimal:

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

Terakhir, kami membuat objek String dari array karakter. Dan kemudian, mengembalikan objek ini sebagai array heksadesimal yang dikonversi.

Sekarang, mari kita pahami bagaimana ini akan bekerja untuk byte negatif -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 Therefore: -4 (base 10) = 1111 1100 (base 2) = fc (base 16)

Perlu juga dicatat bahwa Karakter tersebut. Metode forDigit () selalu mengembalikan karakter huruf kecil.

2.2. Heksadesimal ke Byte

Sekarang, mari kita ubah digit heksadesimal menjadi byte. Seperti yang kita ketahui, satu byte mengandung 8 bit. Oleh karena itu, kita membutuhkan dua digit heksadesimal untuk membuat satu byte .

Pertama-tama, kami akan mengonversi setiap digit heksadesimal menjadi padanan biner secara terpisah.

Dan kemudian, kita perlu menggabungkan dua segmen empat bit untuk mendapatkan byte yang setara:

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

Sekarang, mari kita tulis operasi di 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; }

Mari kita pahami ini, satu operasi dalam satu waktu.

Pertama-tama, kami mengubah karakter heksadesimal menjadi bilangan bulat:

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

Kemudian kami menggeser digit paling signifikan dengan 4 bit. Akibatnya, representasi biner memiliki nol pada empat bit paling tidak signifikan.

Kemudian, kami menambahkan digit terkecil ke dalamnya:

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

Sekarang, mari kita periksa metode toDigit () dengan cermat. Kami menggunakan metode Character.digit () untuk konversi. Jika nilai karakter yang diteruskan ke metode ini bukan digit yang valid di radix yang ditentukan, -1 dikembalikan.

Kami memvalidasi nilai pengembalian dan memberikan pengecualian jika nilai yang tidak valid telah berlalu.

3. Konversi Antara Byte Array dan String Heksadesimal

Pada titik ini, kita tahu cara mengonversi byte menjadi heksadesimal, dan sebaliknya. Mari kita skala algoritma ini dan mengonversi array byte ke / dari String heksadesimal .

3.1. Larik Byte ke String Heksadesimal

Kita perlu melakukan loop melalui array dan menghasilkan pasangan heksadesimal untuk setiap byte:

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

Seperti yang sudah kita ketahui, keluaran akan selalu dalam huruf kecil.

3.2. String Heksadesimal ke Larik Byte

Pertama-tama, kita perlu memeriksa apakah panjang String heksadesimal adalah bilangan genap. Ini karena String heksadesimal dengan panjang ganjil akan menghasilkan representasi byte yang salah.

Sekarang, kita akan melakukan iterasi melalui array dan mengubah setiap pasangan heksadesimal menjadi 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. Menggunakan Kelas BigInteger

Kita bisa membuat objek tipe BigInteger dengan melewatkan array signum dan byte .

Sekarang, kita dapat menghasilkan String heksadesimal dengan bantuan format metode statis yang ditentukan di kelas String :

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

Pada artikel ini, kita mempelajari algoritma konversi antara byte array menjadi string heksadesimal . Kami juga membahas berbagai metode untuk menyandikan array byte ke string hex dan sebaliknya.

Tidak disarankan untuk menambahkan pustaka untuk menggunakan beberapa metode utilitas saja. Oleh karena itu, jika kita belum menggunakan pustaka eksternal, kita harus menggunakan algoritme yang dibahas. Kelas DataTypeConverter adalah cara lain untuk menyandikan / mendekode di antara berbagai tipe data.

Terakhir, kode sumber lengkap dari tutorial ini tersedia di GitHub.