16進数文字列から整数値へ変換するメソッドが、数値型のラッパークラスに用意されています。
ラッパークラス | decodeメソッド | parseXXXメソッド |
---|---|---|
Byte | static Byte decode(String) | static byte parseByte(String, int) |
Short | static Short decode(String) | static short parseShort(String, int) |
Integer | static Integer decode(String) | static int parseInt(String, int) |
Long | static Long decode(String) | static long parseLong(String, int) |
メソッド名 | 対応可能進数 | 文字列表現形式 |
---|---|---|
decodeメソッド | 8 | 0 を先頭に付与 |
10 | そのまま | |
16 | 0xまたは0Xまたは#を先頭に付与 例:"0xCAFEBABE" |
|
parseXXXメソッド | 2〜36進数 | そのまま 16進の例:"CAFEBABE" |
decodeメソッドとparseXXXメソッドの違いは、前者が16進数値文字の前に16進数であることを示す"0x"または"0X"または"#"を付ける必要があることです。
// decode byte byteValue = Byte.decode("0x1a"); short shortValue = Short.decode("0x3AC0"); int intValue = Integer.decode("0XC3C071"); long longValue = Long.decode("#13af21ef"); : // parseXXX byte byteValue = Byte.parseByte("12", 16); short shortValue = Short.parseShort("3AC0", 16); int intValue = Integer.parseInt("C3C071", 16); long longValue = Long.parseLong("13af21ef", 16);
16進数文字列で表現する値は正の値とみなされるので、それぞれの整数型の最大値を越える値となる場合、例外NumberFormatExceptionが発生します。例えばbyte型の場合、値の有効範囲はByte.MIN_VALUE(-128)〜Byte.MAX_VALUE(127)ですから、16進数文字列で"0x80"(10進数で128)以上のものを指定すると例外となります。
// NumberFormatExceptionが発生 byte byteValue = Byte.decode("0x80"); short shortValue = Short.decode("0x8000"); int intValue = Integer.decode("0x80000000"); long longValue = Long.decode("0x8000000000000000"); : byte byteValue = Byte.parseByte("80", 16); short shortValue = Short.parseShort("8000", 16); int intValue = Integer.parseInt("80000000", 16); long longValue = Long.parseLong("8000000000000000", 16);
これは結構深刻な問題で、16進数文字列のダンプデータをbyte配列へ読み込む際などに細工が必要となります。
そこで、より大きい整数型で変換し、本来の整数型へキャストで変換する方法で回避します。
byte byteValue = (byte)Short.decode("0x80"); short shortValue = (short)Integer.decode("0x8000"); int intValue = (int)Long.decode("0x80000000"); : byte byteValue = (byte)Short.parseShort("80", 16); short shortValue = (short)Integer.parseInt("8000", 16); int intValue = (int)Long.parseLong("80000000", 16); long longValue = new BigInteger("8000000000000000", 16).longValue();
プリミティブ型およびそのラッパークラスにはlongより大きい整数型はないので、java.math.BigIntegerを代用します。そのため、少々コードの記述が異なります。
整数を16進数文字列に変換する方法として、以下の2つの方法が思いつきます。
IntegerクラスおよびLongクラスに用意されているtoHexStringメソッドを使用します。BigIntegerクラスの場合、toString(int radix)メソッドで基数に16を指定します。
いずれも、16進数を示す識別(例:"0x")は付与されません。
// 整数ラッパークラスを使用 String hexText1 = Integer.toHexString(intValue); String hexText2 = Long.toHexString(longValue); // BigIntegerの場合 BigInteger bigValue = new BigInteger(...); String hexText3 = bigValue.toString(16);
C言語のprintf関数に慣れているプログラマにはこちらの方法がよいかもしれません。
String text1 = String.format("%x", intValue); String text2 = String.format("%08x", intValue); String text3 = String.format("%#x", intValue);
|
||||||||||||||||
|
IntegerクラスおよびLongクラスに用意されているtoBinaryStringメソッドを使用します。BigIntegerクラスの場合、toString(int radix)メソッドで基数に2を指定します。
// 整数ラッパークラスを使用 String binaryText1 = Integer.toBinaryString(intValue); String binaryText2 = Long.toBinaryString(longValue); // BigIntegerの場合 BigInteger bigValue = new BigInteger(...); String hexText3 = bigValue.toString(2);
演算子 | 演算内容 |
---|---|
& | ビット同士のAND (論理積) |
| | ビット同士のOR (論理和) |
^ | ビット同士のXOR (排他的論理和) |
~ | ビット同士のNOT (反転) |
<< | 左シフト、右側はゼロ埋め |
>> | 右シフト、左側は符号ビット埋め |
>>> | 右シフト、左側はゼロ埋め |
問い合わせ内容 | Integer | Long |
---|---|---|
2の補数2進数表現で1となっているビット数 | Integer.bitCount(intValue) | Long.bitCount(longValue) |
valueの1となっている最も上位ビットと同じ位置だけ1となり残りは0となっている値 | Integer.highestOneBit(intValue) | Long.highestOneBit(longValue) |
valueの1となっている最も下位ビットと同じ位置だけ1となり残りは0となっている値 | Integer.lowestOneBit(intValue) | Long.lowestOneBit(longValue) |
valueの1となっている最も上位ビットの前(左)にある0となっているビット数 | Integer.numberOfLeadingZeros(intValue) | Long.numberOfLeadingZeros(longValue) |
valueの1となっている最も下位ビットの後(右)にある0となっているビット数 | Integer.numberOfTrailingZeros(intValue) | Long.numberOfTrailingZeros(longValue) |
valueのビット並びの逆順 | Integer.reverse(intValue) | Long.reverse(longValue) |
valueのビット並びを左に回転、左側で飛び出たビットは右側最下位ビットに入れる | Integer.rotateLeft(intValue, dist) | Long.rotateLeft(longValue, dist) |
valueのビット並びを右に回転、右側で飛び出たビットは左側最上位ビットに入れる | Integer.rotateRight(intValue, dist) | Long.rotateRight(longValue, dist) |
ネットワーク通信やファイルフォーマットにおいて、バイナリデータを扱う場合にしばしば用いられているのが「ビットフィールド」です。
以下、64bit (long型)の整数値のうち50bit〜38bitの13bit領域に、整数を格納するビットフィールドを読み書きするコード例を紹介します。このビットフィールドは13bitなので、0〜16383の値を表現できます。
63 50 38 0 +------+-------+-----------------+ | | | | +------+-------+-----------------+
このビットフィールドを読み書きする場合は、2つの定数ビットマスクおよびシフト量を定義して、ビット演算を行います。
ビットマスクは、ビットフィールドの該当ビット部分を1、その他を0としたビット列で表わします。上記の例では、64bitのうち、50bit〜38bitが1、それ以外を0とした値となります。
63 56 55 48 47 40 39 32 31 24 23 16 15 8 7 0 +--------+--------+--------+--------+--------+--------+--------+--------+ |00000000|00000111|11111111|11000000|00000000|00000000|00000000|00000000| +--------+--------+--------+--------+--------+--------+--------+--------+ -> 0x0007ffc000000000
private static final long THE_MASK = 0x0007ffc000000000L; private static final int THE_SHIFT = 38; : // 64bit値からのビットフィールド読み出し long code = 0x123456789abcdef0L; int value = (int)( (code & THE_MASK) >>> THE_SHIFT ); // 64bit値へのビットフィールド書き込み int value = 0x1234; code = (code & ~THE_MASK) | ((long)value << THE_SHIFT & THE_MASK);
符号なし整数の場合、シフト演算では最上位ビットの値にかかわらず0で埋めるため、>>>演算子を使用します。
private static final long THE_MASK = 0x0007ffc000000000L; private static final int THE_SHIFT = 38; : // 64bit値からのビットフィールド読み出し long code = 0x123456789abcdef0L; long raw = code & THE_MASK; if ((raw & Long.highestOneBit(THE_MASK)) != 0) { raw |= ~THE_MASK; } int value = (int)(raw >> THE_SHIFT); // 64bit値へのビットフィールド書き込み int value = -1; code = (code & ~THE_MASK) | ((long)value << THE_SHIFT & THE_MASK);
符号付き整数の場合、最上位ビットが1のときが負の値を示します。この例ではビットフィールドに13bit幅を使用しているので、13bit目(64bit中の50bit目)が符号ビットとなります。
しかし、ビットマスクを使用して抜き出した値(上記コードではlong型のraw変数)は、13bit幅ではなく64bit幅の整数なので、負の値とするために51bit目〜63bit目までを1で埋める処理を行っています。なお、副作用で37bit目〜0bit目も1となっていますが、これは次のシフト演算で消えるので無視しても支障ありません。
63 56 55 48 47 40 39 32 31 24 23 16 15 8 7 0 +--------+--------+--------+--------+--------+--------+--------+--------+ |00010010|00110100|01010110|01111000|10011010|10111100|11011110|11110000| +--------+--------+--------+--------+--------+--------+--------+--------+ -> 0x123456789abcdef0 ○ THE_MASK(0x0007ffc000000000)とのビット論理積 63 56 55 48 47 40 39 32 31 24 23 16 15 8 7 0 +--------+--------+--------+--------+--------+--------+--------+--------+ |00000000|00000100|01010110|01000000|00000000|00000000|00000000|00000000| +--------+--------+--------+--------+--------+--------+--------+--------+ -> 0x0004566000000000 ○ 符号ビット(50bit目)が1の場合に、 raw |= ~THE_MASK 63 56 55 48 47 40 39 32 31 24 23 16 15 8 7 0 +--------+--------+--------+--------+--------+--------+--------+--------+ |11111111|11111100|01010110|01111111|11111111|11111111|11111111|11111111| +--------+--------+--------+--------+--------+--------+--------+--------+ -> 0xfffc567fffffffff ○ THE_SHIFT分右算術シフト演算 63 56 55 48 47 40 39 32 31 24 23 16 15 8 7 0 +--------+--------+--------+--------+--------+--------+--------+--------+ |11111111|11111111|11111111|11111111|11111111|11111111|11110001|01011001| +--------+--------+--------+--------+--------+--------+--------+--------+ -> 0xfffffffffffff159 (-3751)
バイナリ・データをJava以外のプログラムとやりとりする場合などで、バイト配列から整数値を生成することが必要になることがあります。
まずは、JavaのI/OストリームAPIを使用してバイトデータから整数へ変換するコードを以下に示します。
バイト配列の並びはビッグエンディアンを前提としています。
byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 }; // (1) ByteArrrayInputStream bais = new ByteArrayInputStream(data); // (2) DataInputStream dis = new DataInputStream(bais); // (3) try { int value = dis.readInt(); // (4) } catch (IOException e) { // ToDo: エラー処理 }
(1)バイト配列を生成するときに、byte型は-128〜+127の範囲であるため、128(0x80)以上の値を代入するには(byte)型へのキャストを明示的に記述する必要があります。
(2) I/OストリームのAPIには、メモリ上にあるバイト配列を入出力先として使うクラスが用意されています。
(3)整数型の読み書きはI/OストリームではDataInputStream/DataOutputStreamを使用します。
(4)整数(int)型を読み込むには、DataInputStreamクラスのreadIntメソッドを使用します。なお、DataInputStreamでは、データがビッグエンディアンの並び順となっていることを前提としています。
Java NIO APIを使用して、バイトデータから整数へ変換するコードを以下に示します。
以下はバイト配列の並びをデフォルトのビッグエンディアンで使用しています。リトルエンディアンとして処理することもできます。
byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 }; ByteBuffer buffer = ByteBuffer.wrap(data); int value = buffer.getInt();
上記のコードでint型の変数valueには、0x87654321が入ります。
byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 }; ByteBuffer buffer = ByteBuffer.wrap(data); long value = buffer.getInt() & 0xffff_ffffL;
byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 }; ByteBuffer buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.put(data); buffer.flip(); int value = buffer.getInt();
上記のコードでint型の変数valueには、0x21436587が入ります。
byte[] buf = ...; short u8 = buf[0] & 0xff; int u16 = (buf[0] & 0xff) << 8 | (buf[1] & 0xff); long u32 = (buf[0] & 0xff) << 24 | (buf[1] & 0xff) << 16 | (buf[2] & 0xff) << 8 | (buf[3] & 0xff;
byte[] buf = ...; byte s8 = buf[0]; short s16 = buf[0] << 8 | (buf[1] & 0xff); int s32 = buf[0] << 24 | (buf[1] & 0xff) << 16 | (buf[2] & 0xff) << 8 | (buf[3] & 0xff;