1. Gambaran keseluruhan
Dalam tutorial ini, kita akan melihat cara yang berbeza untuk menukar array byte ke String heksadesimal , dan sebaliknya.
Kami juga akan memahami mekanisme penukaran dan menulis pelaksanaan kami untuk mencapainya.
2. Menukar Antara Byte dan Heksadesimal
Pertama sekali, mari kita lihat logik penukaran antara nombor bait dan heksadesimal.
2.1. Byte hingga Heksadesimal
Byte adalah bilangan bulat bertanda 8 bit di Java. Oleh itu, kita perlu menukar setiap segmen 4-bit ke hex secara berasingan dan menggabungkannya . Oleh itu, kita akan mendapat dua aksara perenambelasan setelah penukaran.
Sebagai contoh, kita dapat menulis 45 sebagai 0010 1101 dalam binari, dan setara heksadesimal akan menjadi "2d":
0010 = 2 (base 10) = 2 (base 16) 1101 = 13 (base 10) = d (base 16) Therefore: 45 = 0010 1101 = 0x2d
Mari kita laksanakan logik mudah 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 fahami kod di atas dengan menganalisis setiap operasi. Pertama sekali, kami membuat array char panjang 2 untuk menyimpan output:
char[] hexDigits = new char[2];
Seterusnya, kami mengasingkan bit pesanan lebih tinggi dengan menukar 4 bit kanan. Kemudian, kami menggunakan topeng untuk mengasingkan urutan bawah 4 bit. Penutup diperlukan kerana nombor negatif ditunjukkan secara dalaman sebagai pelengkap nombor positif dua:
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
Kemudian kami menukar baki 4 bit menjadi heksadesimal:
hexDigits[1] = Character.forDigit((num & 0xF), 16);
Akhirnya, kami membuat objek String dari array char. Dan kemudian, mengembalikan objek ini sebagai array heksadesimal yang ditukar.
Sekarang, mari kita fahami bagaimana ini berfungsi untuk bait 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 diperhatikan bahawa Watak. kaedah forDigit () selalu mengembalikan huruf kecil.
2.2. Heksadesimal hingga Bait
Sekarang, mari kita menukar digit heksadesimal menjadi bait. Seperti yang kita ketahui, bait mengandungi 8 bit. Oleh itu, kita memerlukan dua digit heksadesimal untuk membuat satu bait .
Pertama sekali, kami akan menukar setiap digit perenambelasan menjadi setara binari secara berasingan.
Dan kemudian, kita perlu menggabungkan dua empat segmen bit untuk mendapatkan bait yang setara:
Hexadecimal: 2d 2 = 0010 (base 2) d = 1101 (base 2) Therefore: 2d = 0010 1101 (base 2) = 45
Sekarang, mari 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 fahami ini, satu operasi pada satu masa.
Pertama sekali, kami menukar aksara perenambelasan menjadi bilangan bulat:
int firstDigit = toDigit(hexString.charAt(0)); int secondDigit = toDigit(hexString.charAt(1));
Kemudian kami meninggalkan digit yang paling ketara yang berubah dengan 4 bit. Akibatnya, perwakilan binari mempunyai sifar pada empat bit paling penting.
Kemudian, kami menambahkan digit yang paling tidak signifikan kepadanya:
return (byte) ((firstDigit << 4) + secondDigit);
Sekarang, mari kita teliti kaedah toDigit () . Kami menggunakan kaedah Character.digit () untuk penukaran. Sekiranya nilai watak yang diteruskan ke kaedah ini bukan digit yang sah dalam jejari yang ditentukan, -1 dikembalikan.
Kami mengesahkan nilai pengembalian dan membuang pengecualian jika nilai tidak sah dilalui.
3. Menukar Antara Susunan Bait dan Rentetan Heksadesimal
Pada ketika ini, kita tahu bagaimana menukar bait ke heksadesimal, dan sebaliknya. Mari skala algoritma ini dan ubah array bait ke / dari String heksadesimal .
3.1. Array Byte ke Rentetan Heksadesimal
Kita perlu melengkapkan array dan menghasilkan pasangan heksadesimal untuk setiap bait:
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
Dalam artikel ini, kami mempelajari algoritma penukaran antara array byte ke String heksadesimal . Kami juga membincangkan pelbagai kaedah untuk mengekod array byte ke rentetan hex dan sebaliknya.
Tidak dinasihatkan untuk menambahkan perpustakaan untuk menggunakan beberapa kaedah utiliti sahaja. Oleh itu, jika kita belum menggunakan perpustakaan luaran, kita harus menggunakan algoritma yang dibincangkan. The DataTypeConverter kelas adalah cara lain untuk mengekod / decode antara pelbagai jenis data.
Akhirnya, kod sumber lengkap tutorial ini boleh didapati di GitHub.