Converting Between Byte Arrays and Hexadecimal Strings in Java
Overview
このチュートリアルでは、バイト配列を16進数の文字列に変換する、またはその逆を行うさまざまな方法を見ていきます。
また、変換のメカニズムを理解し、それを実現するための実装を記述します。
バイトと16進数の変換
まず、バイトと16進数の間の変換ロジックを見てみましょう
2.1. バイトから16進数
バイトはJavaでは8ビットの符号付き整数です。 そのため、各4ビットのセグメントを個別に16進数に変換し、それらを連結する必要があります。 その結果、変換後は2つの16進数の文字が得られます。
例えば、45を2進法で0010 1101と書くと、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; hexDigits = Character.forDigit((num >> 4) & 0xF, 16); hexDigits = Character.forDigit((num & 0xF), 16); return new String(hexDigits);}
さて、上記のコードを各操作を分析して理解しましょう。
char hexDigits = new char;
次に、4ビットを右シフトして高次のビットを分離します。 そして、低次の4ビットを分離するためにマスクを適用しました。
hexDigits = Character.forDigit((num >> 4) & 0xF, 16);
そして、残りの4ビットを16進数に変換します。
hexDigits = Character.forDigit((num & 0xF), 16);
最後に、char配列からStringオブジェクトを作成します。
ここで、負のバイト -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)
また、Character.forDigit() メソッドは常に小文字を返すことにも注目してください。 16進法からバイトへ
さて、16進法の数字をバイトに変換してみましょう。 ご存知のように、1バイトには8ビットが含まれています。
まず、16進数の各桁を別々に2進数に変換します。
そして、2つの4ビットセグメントを連結して、バイトに相当するものを得る必要があります。
Hexadecimal: 2d2 = 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ビット左にシフトします。
続いて、最下位の桁を追加します。
return (byte) ((firstDigit << 4) + secondDigit);
ここで、toDigit()メソッドを詳しく見てみましょう。 変換にはCharacter.digit()メソッドを使用しています。
戻り値を検証し、無効な値が渡された場合は例外をスローしています。
バイト配列と16進数文字列の変換
この時点で、バイトを16進数に変換する方法と、その逆の方法がわかりました。 このアルゴリズムを拡張して、バイト配列を16進文字列との間で変換してみましょう。
3.1. バイト配列から 16 進文字列へ
配列をループして、各バイトに 16 進文字列のペアを生成する必要があります。 16進文字列からバイト配列へ
まず、16進文字列の長さが偶数であるかどうかを確認する必要があります。
ここで、配列を繰り返し、各16進数のペアをバイトに変換します。
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;}
BigIntegerクラスの使用
signumとバイト配列を渡すことで、BigInteger型のオブジェクトを作成できます。
ここで、Stringクラスに定義されているスタティックメソッドformatの助けを借りて、16進文字列を生成することができます:
public String encodeUsingBigIntegerStringFormat(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return String.format( "%0" + (bytes.length << 1) + "x", bigInteger);}
提供されたformatは、ゼロパディングされた小文字の16進文字列を生成します。
別の方法として、BigInteger の toString() メソッドを使用することもできましたが、この場合は、「x」を「X」に置き換えることで、大文字の文字列を生成することができます。 toString() メソッドを使用した場合の微妙な違いは、出力が先頭のゼロでパッドされていないことです。
public String encodeUsingBigIntegerToString(byte bytes) { BigInteger bigInteger = new BigInteger(1, bytes); return bigInteger.toString(16);}
さて、16 進数の文字列からバイト配列への変換を見てみましょう。
ToByteArray()メソッドは、追加の符号ビットを生成します。この追加のビットを処理するための特定のコードを記述しました。
従って、変換のために BigInteger クラスを使用する前に、これらの詳細を認識する必要があります。 これは、Java 8までは標準ライブラリの一部です。
DataTypeConverterクラスを使用した実装を見てみましょう。
public String encodeUsingDataTypeConverter(byte bytes) { return DatatypeConverter.printHexBinary(bytes);}public byte decodeUsingDataTypeConverter(String hexString) { return DatatypeConverter.parseHexBinary(hexString);}
上に表示されているように、DataTypeConverterクラスを使用するのはとても便利です。 printHexBinary()メソッドの出力は常に大文字になります。
この方法を選択する前に、このクラスが実行時に利用可能であることを確認する必要があります。
Apache の Commons-Codec ライブラリを使用する
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);}
encodeHexString の出力は常に小文字です。
GoogleのGuavaライブラリを使用する
BaseEncodingクラスを使用して、バイト配列を16進文字列にエンコードおよびデコードする方法を見てみましょう:
public String encodeUsingGuava(byte bytes) { return BaseEncoding.base16().encode(bytes);}public byte decodeUsingGuava(String hexString) { return BaseEncoding.base16() .decode(hexString.toUpperCase());}
BaseEncodingは、デフォルトでは大文字を使用してエンコードおよびデコードします。
結論
この記事では、バイト配列から16進数文字列への変換アルゴリズムを学びました。
いくつかのユーティリティー メソッドを使用するためだけにライブラリを追加することはお勧めできません。
いくつかのユーティリティーメソッドを使用するためだけにライブラリを追加することはお勧めできません。 DataTypeConverterクラスは、さまざまなデータタイプの間でエンコード/デコードを行うもうひとつの方法です。