Javaにおけるバイト配列と16進文字列の間の変換

1.概要

このチュートリアルでは、バイト配列を16進数の__String、およびその逆に変換するさまざまな方法について説明します。

また、変換メカニズムを理解し、これを実現するための実装を作成します。

2.バイトと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[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ビットを含んでいます。したがって、1バイトを作成するには、2桁の16進数が必要です。

まず最初に、各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() メソッドを使用しています。 ** このメソッドに渡された文字の値が指定された基数の有効な数字ではない場合、-1が返されます。

無効な値が渡された場合は、戻り値を検証して例外をスローします。

3.バイト配列と16進数__文字列間の変換

これで、バイトから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進数ペアを1バイトに変換します。

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 クラスを使用する

signumとバイト配列を渡すことで BigInteger 型のオブジェクトを作成できます。

これで、 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() メソッドを使用することもできます。 toString() メソッドを使用することの微妙な違いは、出力に先行ゼロが埋め込まれていないことです。

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;
}
  • toByteArray() メソッドは追加の符号ビットを生成します** 。この追加のビットを処理するための特定のコードを書きました。

したがって、変換に BigInteger クラスを使用する前に、これらの詳細に注意する必要があります。

5. DataTypeConverter クラスを使う

DataTypeConverter クラスはJAXBライブラリに付属しています。これは、Java 8までの標準ライブラリの一部です。Java9以降、 java.xml.bind モジュールを明示的にランタイムに追加する必要があります。

DataTypeConverter クラスを使用した実装を見てみましょう。

public String encodeUsingDataTypeConverter(byte[]bytes) {
    return DatatypeConverter.printHexBinary(bytes);
}

public byte[]decodeUsingDataTypeConverter(String hexString) {
    return DatatypeConverter.parseHexBinary(hexString);
}

上記のように、 DataTypeConverter クラスを使用すると非常に便利です。 printHexBinary() メソッドの 出力は常に大文字 です。このクラスはデータ型変換のためのprintとparseメソッドのセットを提供します。

このアプローチを選択する前に、クラスが実行時に利用可能になることを確認する必要があります。

6. ApacheのCommons-Codecライブラリを使う

Apache commons-codecライブラリに付属のhttps://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Hex.html[ Hex ]クラスを使用することができます。

public String encodeUsingApacheCommons(byte[]bytes)
  throws DecoderException {
    return Hex.encodeHexString(bytes);
}

public byte[]decodeUsingApacheCommons(String hexString)
  throws DecoderException {
    return Hex.decodeHex(hexString);
}

__encodeHexStringの出力は常に小文字になります。

7. GoogleのGuavaライブラリを使用する

BaseEncoding クラスをバイト配列のエンコードおよびデコードに使用する方法を見てみましょう。 16進数の String:

public String encodeUsingGuava(byte[]bytes) {
    return BaseEncoding.base16().encode(bytes);
}

public byte[]decodeUsingGuava(String hexString) {
    return BaseEncoding.base16()
      .decode(hexString.toUpperCase());
}
  • BaseEncoding は、デフォルトで大文字を使用してエンコードおよびデコードします** 。小文字を使用する必要がある場合は、静的メソッドhttps://google.github.io/guava/releases/16.0/api/docs/com/google/common/io/BaseEncoding.html#を使用して新しいエンコードインスタンスを作成する必要があります。 lowerCase()[小文字]。

8.まとめ

この記事では、バイト配列から16進数の String への変換アルゴリズムを学びました。また、バイト配列を16進文字列にエンコードする方法、およびその逆の方法についても説明しました。

いくつかのユーティリティメソッドだけを使用するようにライブラリを追加することはお勧めできません。したがって、外部ライブラリをまだ使用していない場合は、説明したアルゴリズムを使用してください。 DataTypeConverter クラスは、さまざまなデータ型間のエンコード/デコードを行うもう1つの方法です。

最後に、このチュートリアルの完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/algorithms-miscellaneous-2[GitHubで入手可能]です。