1. Overview
1.概述
In this tutorial, we’ll take a look at different ways to convert a byte array to a hexadecimal String, and vice versa.
在本教程中,我们将看看将字节数组转换为十六进制String,的不同方法,反之亦然。
We’ll also understand the conversion mechanism and write our implementation to achieve this.
我们还将了解转换机制,并写出我们的实现方法来实现这一目标。
2. Converting Between Byte and Hexadecimal
2.字节和十六进制之间的转换
First of all, let’s take a look at the conversion logic between byte and hexadecimal numbers.
首先,让我们来看看字节数和十六进制数之间的转换逻辑。
2.1. Byte to Hexadecimal
2.1 字节转十六进制
The bytes are 8 bit signed integers in Java. Therefore, we need to convert each 4-bit segment to hex separately and concatenate them. Consequently, we’ll get two hexadecimal characters after conversion.
在Java中,这些字节是8位有符号的整数。因此,我们需要将每个4位段分别转换为十六进制,然后将它们连接起来。因此,转换后我们会得到两个十六进制的字符。
For instance, we can write 45 as 0010 1101 in binary, and the hexadecimal equivalent will be “2d”:
例如,我们可以把45写成二进制的0010 1101,而十六进制的对应值是 “2d”。
0010 = 2 (base 10) = 2 (base 16)
1101 = 13 (base 10) = d (base 16)
Therefore: 45 = 0010 1101 = 0x2d
Let’s implement this simple logic in Java:
让我们用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);
}
Now, let’s understand the above code by analyzing each operation. First of all, we created a char array of length 2 to store the output:
现在,让我们通过分析每个操作来理解上述代码。首先,我们创建了一个长度为2的char数组来存储输出。
char[] hexDigits = new char[2];
Next, we isolated higher order bits by right shifting 4 bits. And then, we applied a mask to isolate lower order 4 bits. Masking is required because negative numbers are internally represented as two’s complement of the positive number:
接下来,我们通过右移4位来隔离高阶位。然后,我们应用一个掩码来隔离低阶4位。屏蔽是必要的,因为负数在内部被表示为正数的two’s complement。
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
Then we convert the remaining 4 bits to hexadecimal:
然后我们将剩余的4位转换为16进制。
hexDigits[1] = Character.forDigit((num & 0xF), 16);
Finally, we create a String object from the char array. And then, returned this object as converted hexadecimal array.
最后,我们从char数组中创建一个String对象。然后,将此对象作为转换后的十六进制数组返回。
Now, let us understand how this will work for a negative byte -4:
现在,让我们了解一下这对一个负的字节-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)
It’s also worth noting that the Character.forDigit() method always returns lowercase characters.
还值得注意的是,Character.forDigit()方法总是返回小写字符。
2.2. Hexadecimal to Byte
2.2 十六进制转字节
Now, let’s convert a hexadecimal digit to byte. As we know, a byte contains 8 bits. Therefore, we need two hexadecimal digits to create one byte.
现在,让我们把十六进制的数字转换成字节。正如我们所知,一个字节包含8位。因此,我们需要两个十六进制数字来创建一个字节。
First of all, we’ll convert each hexadecimal digit into binary equivalent separately.
首先,我们要把每个十六进制数字分别转换为二进制等价物。
And then, we need to concatenate the two four bit-segments to get the byte equivalent:
然后,我们需要将这两个四位段串联起来,得到相当于字节的数据。
Hexadecimal: 2d
2 = 0010 (base 2)
d = 1101 (base 2)
Therefore: 2d = 0010 1101 (base 2) = 45
Now, let’s write the operation in Java:
现在,让我们用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;
}
Let’s understand this, one operation at a time.
让我们理解这一点,一次一个操作。
First of all, we converted hexadecimal characters into integers:
首先,我们将十六进制的字符转换成整数。
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
Then we left shifted most significant digit by 4 bits. Consequently, the binary representation has zeros at four least significant bits.
然后我们把最重要的数字左移4位。因此,二进制表示法在四个最不重要的位上有零。
Then, we added the least significant digit to it:
然后,我们把最小有效数字加到它上面。
return (byte) ((firstDigit << 4) + secondDigit);
Now, let’s examine the toDigit() method closely. We are using the Character.digit() method for conversion. If the character value passed to this method is not a valid digit in the specified radix, -1 is returned.
现在,让我们仔细检查toDigit()方法。我们正在使用Character.digit()方法进行转换。如果传递给这个方法的字符值不是指定小数点的有效数字,就会返回-1。
We’re validating the return value and throwing an exception if an invalid value was passed.
我们正在验证返回值,如果传递了一个无效的值,则抛出一个异常。
3. Converting Between Byte Arrays and Hexadecimal Strings
3.字节数组与十六进制字符串之间的转换
At this point, we know how to convert a byte to the hexadecimal, and vice versa. Let’s scale this algorithm and convert byte array to/from hexadecimal String.
在这一点上,我们知道如何将一个字节转换成十六进制,反之亦然。让我们扩展这个算法,将字节数组转换为/从十六进制String。
3.1. Byte Array to Hexadecimal String
3.1.字节数组转十六进制字符串
We need to loop through the array and generate hexadecimal pair for each 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();
}
As we already know, the output will always be in lowercase.
正如我们已经知道的,输出将总是小写的。
3.2. Hexadecimal String to Byte Array
3.2.十六进制字符串到字节数组
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.
首先,我们需要检查十六进制String的长度是否为偶数。这是因为长度为奇数的十六进制String将导致错误的字节表示。
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
4.使用BigInteger类
We can create an object of type BigInteger by passing a signum and byte array.
我们可以通过传递一个符号和字节数创建一个BigInteger类型的对象。
Now, we can generate the hexadecimal String with the help of static method format defined in String class:
现在,我们可以在String类中定义的静态方法格式的帮助下生成十六进制的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”.
所提供的格式将生成一个零填充的小写十六进制字符串。我们也可以通过用 “X “替换 “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:
另外,我们可以使用toString()方法,该方法来自BigInteger。使用toString()方法的微妙区别在于,输出不会被填充前导零。
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.
toByteArray()方法产生一个额外的符号位。我们已经编写了专门的代码来处理这个额外的位。
Hence, we should be aware of these details before using the BigInteger class for the conversion.
因此,在使用BigInteger类进行转换之前,我们应该注意到这些细节。
5. Using the DataTypeConverter Class
5.使用DataTypeConverter类
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.
DataTypeConverter类是由JAXB库提供的。这是在Java 8之前的标准库的一部分。从Java 9开始,我们需要将java.xml.bind模块明确添加到运行时。
Let’s take a look at implementation using the DataTypeConverter class:
让我们看一下使用DataTypeConverter类的实现。
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.
如上所示,使用DataTypeConverter类非常方便。printHexBinary()方法的输出总是大写的。这个类为数据类型转换提供了一组打印和解析方法。
Before choosing this approach, we need to make sure the class will be available at runtime.
在选择这种方法之前,我们需要确保该类在运行时是可用的。
6. Using Apache’s Commons-Codec Library
6.使用Apache的Commons-Codec库
We can use the Hex class supplied with the Apache commons-codec library:
我们可以使用Apache commons-codec库中提供的Hex类。
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.
encodeHexString的输出总是小写的。
7. Using Google’s Guava Library
7.使用谷歌的Guava库
Let’s take a look at how BaseEncoding class can be used for encoding and decoding byte array to the hexadecimal String:
让我们看看BaseEncoding类如何用于将字节数组编码和解码为十六进制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.
BaseEncoding默认使用大写字符进行编码和解码。如果我们需要使用小写字符,应该使用静态方法小写创建一个新的编码实例。
8. Conclusion
8.结语
In this article, we learned the conversion algorithm between byte array to hexadecimal String. We also discussed various methods to encode byte array to hex string and vice versa.
在这篇文章中,我们学习了字节数组到十六进制String之间的转换算法。我们还讨论了将字节数组编码为十六进制字符串的各种方法,反之亦然。
It isn’t advised to add a library to use a couple of utility methods only. Therefore, if we aren’t using the external libraries already, we should use the algorithm discussed. The DataTypeConverter class is another way to encode/decode between various data types.
我们不建议添加一个库来只使用几个实用的方法。因此,如果我们还没有使用外部库,我们应该使用所讨论的算法。DataTypeConverter类是另一种在各种数据类型之间进行编码/解码的方法。
Finally, the complete source code of this tutorial is available on GitHub.
最后,本教程的完整源代码在GitHub上提供。