web-dev-qa-db-ja.com

Javaで符号なしバイトを作成できますか

符号付きバイトを符号なしで変換しようとしています。問題は、受信しているデータが符号なしであり、Javaが符号なしバイトをサポートしていないため、データを読み取るときに符号付きとして扱うことです。

私はそれが私がStack Overflowから得た以下の解決策によってそれを変換しようとしました。

public static int unsignedToBytes(byte a)
{
    int b = a & 0xFF;
    return b;
}

しかし、それがまたバイトで変換されるとき、私は同じ署名されたデータを得ます。このデータを1バイトだけをパラメータとして受け取るJavaの関数へのパラメータとして使用しようとしているので、他のデータ型は使用できません。どうすればこの問題を解決できますか?

168
dln

プリミティブがJavaで署名されているという事実は、それらがメモリ/トランジットでどのように表現されているかとは無関係です。 「これは署名されている」または「これは署名されていない」と言う魔法のフラグはありません。

プリミティブが署名されているので、Javaコンパイラは、+ 127よりも大きい(または-128よりも小さい)値をユーザーが代入できないようにします。ただし、これを達成するためにint(またはshort)をダウンキャストするのを止めるものは何もありません。

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)

/*
 * Will print a negative int -56 because upcasting byte to int does
 * so called "sign extension" which yields those bits:
 * 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
 *
 * But you could still choose to interpret this as +200.
 */
System.out.println(b); // "-56"

/*
 * Will print a positive int 200 because bitwise AND with 0xFF will
 * zero all the 24 most significant bits that:
 * a) were added during upcasting to int which took place silently
 *    just before evaluating the bitwise AND operator.
 *    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
 * b) were set to 1s because of "sign extension" during the upcasting
 *
 * 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
 * &
 * 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
 * =======================================
 * 0000 0000 0000 0000 0000 0000 1100 1000 (200)
 */
System.out.println(b & 0xFF); // "200"

/*
 * You would typically do this *within* the method that expected an 
 * unsigned byte and the advantage is you apply `0xFF` only once
 * and than you use the `unsignedByte` variable in all your bitwise
 * operations.
 *
 * You could use any integer type longer than `byte` for the `unsignedByte` variable,
 * i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
 * it would get casted to `int` anyway.
 */
void printUnsignedByte(byte b) {
    int unsignedByte = b & 0xFF;
    System.out.println(unsignedByte); // "200"
}
180
Adamski

Java言語はunsignedキーワードのようなものは何も提供していません。言語仕様に従ったbyteは、−128から127の間の値を表します。例えば、byteintにキャストされる場合、Javaは最初のビットを符号として解釈し、 符号拡張 を使用します。

とはいえ、byteを単純に8ビットと見なし、それらのビットを0から255までの値として解釈することを妨げるものは何もありません。他の人の方法で解釈を強制することはできません。メソッドがbyteを受け付ける場合は、特に明記しない限り、そのメソッドは−128から127までの値を受け入れます。

ここにあなたの便利のための2つの役に立つ変換/操作があります:

Intとの間の変換

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value
// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(または、Java 8+を使用している場合は、 Byte.toUnsignedInt を使用してください。)

解析/フォーマット

最善の方法は、上記の変換を使用することです。

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");
// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

算術

2の補数表現は足し算、引き算および掛け算のために "ちょうど働きます":

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;

byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

除算には、オペランドの手動変換が必要です。

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
39
aioobe

Javaにはプリミティブの符号なしバイトはありません。通常はもっと大きな型にキャストすることです:

int anUnsignedByte = (int) aSignedByte & 0xff;
34
Peter Knego

私は他の答えがメモリ表現をカバーしてきたそしてあなたがこれらをどう扱うかはあなたがそれを使うことを計画する方法の文脈に依存すると思う。私はそれを付け加えます Java 8はunsigned型を扱うためのいくつかのサポートを追加しました 。この場合は、 Byte.toUnsignedInt を使用できます。

int unsignedInt = Byte.toUnsignedInt(myByte);
20
mkobit

あなたがそれを印刷したい場合は、サイドノート、あなただけ言うことができます

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
4
Kyle Kinkade

Adamskiが最善の答えを提供したが、それは完全ではないので、それは私がしていない詳細を説明しているので、彼の返事を読む。

符号なしバイトを渡す必要があるシステム関数がある場合は、符号付きバイトを自動的に符号なしバイトとして扱うため、符号付きバイトを渡すことができます。

つまり、システム関数が符号なしバイトとして192 168 0 1のように4バイトを必要とする場合は、-64 -88 0 1を渡すことができます。それらを関数に渡すことで署名が解除されるため、関数は機能します。 。

ただし、Java.ioのreadメソッドの中にはシグニチャのないバイトをintとして返すものもありますが、システム関数はプラットフォーム間の互換性のためにクラスの背後に隠されているため、この問題が発生する可能性は低いです。

この動作を確認したい場合は、符号付きバイトをファイルに書き込み、それらを符号なしバイトとして読み戻してみてください。

1
Ewe Loon

符号付きバイトを渡さなければならない関数がある場合、あなたが符号なしバイトを渡した場合、あなたはそれが何をすると期待しますか?

他のデータ型を使用できないのはなぜですか。

通常、バイトを符号なしバイトとして単純な変換または変換なしで使用できます。それはすべてそれがどのように使われるかに依存します。あなたはそれに対して何をするつもりかを明確にする必要があるでしょう。

0
Peter Lawrey

あなたもすることができます:

public static int unsignedToBytes(byte a)
{
    return (int) ( ( a << 24) >>> 24);
}    

説明:

a = (byte) 133;と言いましょう

メモリ上では、 "1000 0101"(16進数で0x85)として保存されています。

つまり、その表現は符号なし= 133、符号付き= - 123(2の補数として)に変換されます。

a << 24

左シフトが左に24ビット実行されると、resultは4バイトの整数になり、これは次のように表されます。

"10000101 00000000 00000000 00000000"(または16進数の "0x85000000")

それなら

(a << 24)>>> 24

そしてそれは再び右側の24ビットにシフトしますが、先行ゼロで埋められます。その結果、

"00000000 00000000 00000000 10000101"(または16進数の "0x00000085")

そしてそれは133に等しい符号なしの表現です。

a = (int) a;をキャストしようとすると、2の補数表現のbyteが保持され、intとして2の補数としても格納されます。

(int) "10000101" ---> "11111111 11111111 11111111 10000101"

-123

0
mark_infinite

Javaには符号なしバイトはありませんが、バイトを表示したい場合は、次のようにします。

int myInt = 144;

byte myByte = (byte) myInt;

char myChar = (char) (myByte & 0xFF);

System.out.println("myChar :" + Integer.toHexString(myChar));

出力:

myChar : 90

詳細については、Javaで16進数/バイト値を表示する方法を確認してください。

0
Jyo the Whiff

Javaが言語に符号なしバイトを含まなかったのは(Cから来る)いらいらしているように見えるかもしれませんが、単純な "b&0xFF"操作で(符号付き)バイトbの符号なしの値が(まれ)それが実際に必要とされる状況。ビットは実際には変更されません - 単に解釈です(これは、例えば値に対していくつかの数学演算を行う場合にのみ重要です)。

0
Bob

あなたがこのような何かを探していると思ったら。

public static char toUnsigned(byte b) {
    return (char) (b >= 0 ? b : 256 + b);
}
0