Javaでのバイト配列と16進数文字列間の変換
1. 概要
このチュートリアルでは、バイト配列を16進数のString,に、またはその逆に変換するさまざまな方法を見ていきます。
また、変換メカニズムを理解し、これを実現するための実装を記述します。
2. バイトと16進数の間の変換
まず、バイト数と16進数の間の変換ロジックを見てみましょう。
2.1. バイトから16進数
バイトはJavaの8ビット符号付き整数です。 したがって、convert each 4-bit segment to hex separately and concatenate themにする必要があります。 したがって、変換後に2つの16進文字が取得されます。
たとえば、バイナリで0010 1101として45を書き込むことができ、16進数の同等物は「2d」になります。
0010 = 2 (base 10) = 2 (base 16)
1101 = 13 (base 10) = d (base 16)
Therefore: 45 = 0010 1101 = 0x2d
この単純なロジックを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);
}
それでは、各操作を分析して、上記のコードを理解しましょう。 まず、出力を保存するために長さ2のchar配列を作成しました。
char[] hexDigits = new char[2];
次に、4ビットを右にシフトして、上位ビットを分離しました。 そして、マスクを適用して下位4ビットを分離しました。 負の数は内部的に正の数の2の補数として表されるため、マスキングが必要です。
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
次に、残りの4ビットを16進数に変換します。
hexDigits[1] = Character.forDigit((num & 0xF), 16);
最後に、char配列からStringオブジェクトを作成します。 そして、このオブジェクトを変換された16進数配列として返しました。
次に、負のバイト-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)
Character.forDigit _()_メソッドは常に小文字を返すことにも注意してください。
2.2. 16進数からバイト
それでは、16進数をバイトに変換してみましょう。 知っているように、バイトには8ビットが含まれます。 したがって、we need two hexadecimal digits to create one byte。
まず、16進数の各桁を2進数に個別に変換します。
次に、2つの4つのビットセグメントを連結して、同等のバイトを取得する必要があります。
Hexadecimal: 2d
2 = 0010 (base 2)
d = 1101 (base 2)
Therefore: 2d = 0010 1101 (base 2) = 45
それでは、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;
}
これを、一度に1つの操作で理解しましょう。
まず、16進文字を整数に変換しました。
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
次に、最上位桁を左に4ビットシフトしました。 そのため、バイナリ表現の4つの最下位ビットにゼロがあります。
次に、最下位桁を追加しました。
return (byte) ((firstDigit << 4) + secondDigit);
それでは、toDigit()メソッドを詳しく調べてみましょう。 変換にはCharacter.digit()メソッドを使用しています。 If the character value passed to this method is not a valid digit in the specified radix, -1 is returned.
戻り値を検証し、無効な値が渡された場合は例外をスローします。
3. バイト配列と16進数のStrings間の変換
この時点で、バイトを16進数に変換する方法と、その逆の方法を知っています。 このアルゴリズムをスケーリングして、バイト配列を16進数のStringとの間で変換してみましょう。
3.1. バイト配列から16進数のString
配列をループして、バイトごとに16進数のペアを生成する必要があります。
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();
}
すでに知っているように、出力は常に小文字になります。
3.2. 16進文字列からバイト配列
まず、16進数のStringの長さが偶数かどうかを確認する必要があります。 これは、長さが奇数の16進数のStringが、誤ったバイト表現になるためです。
次に、配列を反復処理して、各16進ペアをバイトに変換します。
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. BigIntegerクラスの使用
create an object of type BigInteger by passing a signum and byte arrayできます。
これで、Stringクラスで定義された静的メソッド形式を使用して16進数のStringを生成できます。
public String encodeUsingBigIntegerStringFormat(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
return String.format(
"%0" + (bytes.length << 1) + "x", bigInteger);
}
提供されている形式では、ゼロが埋め込まれた小文字の16進数のString.が生成されます。「x」を「X」に置き換えることで大文字の文字列を生成することもできます。
または、BigIntegerからtoString()メソッドを使用することもできます。 微妙な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);
}
それでは、16進数のStringからbyteへの配列変換を見てみましょう。
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。 この追加ビットを処理するための特定のコードを作成しました。
したがって、変換にBigIntegerクラスを使用する前に、これらの詳細を知っておく必要があります。
5. DataTypeConverterクラスの使用
DataTypeConverterクラスはJAXBライブラリで提供されます。 これは、Java 8までの標準ライブラリの一部です。 Java 9以降では、java.xml.bindモジュールをランタイムに明示的に追加する必要があります。
DataTypeConverterクラスを使用した実装を見てみましょう。
public String encodeUsingDataTypeConverter(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
public byte[] decodeUsingDataTypeConverter(String hexString) {
return DatatypeConverter.parseHexBinary(hexString);
}
上に表示されているように、DataTypeConverterクラスを使用すると非常に便利です。 output of the printHexBinary() method is always in uppercase。 このクラスは、データ型変換用の一連の印刷および解析メソッドを提供します。
このアプローチを選択する前に、実行時にクラスを使用できるようにする必要があります。
6. Apacheのコモンズの使用-コーデックライブラリ
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);
}
output of encodeHexString is always in lowercase。
7. GoogleのGuavaライブラリを使用する
バイト配列を16進数のString:にエンコードおよびデコードするためにBaseEncodingクラスをどのように使用できるかを見てみましょう。
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。 小文字を使用する必要がある場合は、静的メソッドlowercaseを使用して新しいエンコーディングインスタンスを作成する必要があります。
8. 結論
この記事では、バイト配列から16進数のStringへの変換アルゴリズムについて学習しました。 また、バイト配列を16進数文字列にエンコードするさまざまな方法についても説明しました。
いくつかのユーティリティメソッドのみを使用するためにライブラリを追加することはお勧めしません。 したがって、外部ライブラリをまだ使用していない場合は、説明したアルゴリズムを使用する必要があります。 DataTypeConverterクラスは、さまざまなデータ型間でエンコード/デコードするもう1つの方法です。
最後に、このチュートリアルの完全なソースコードはavailable on GitHubです。