我们可以用Java做无符号字节吗


185

我正在尝试将无符号字节转换为带符号字节。问题是我接收的数据是无符号的,并且Java不支持无符号字节,因此当它读取数据时,会将其视为已签名。

我尝试通过以下来自Stack Overflow的解决方案将其转换。

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

但是当再次将其转换为字节时,我得到了相同的签名数据。我正在尝试将此数据用作Java函数的参数,该Java函数仅接受字节作为参数,因此无法使用任何其他数据类型。我该如何解决这个问题?


2
番石榴:UnsignedBytes.toint(byte value)
jacktrades

20
java.lang.Byte.toUnsignedInt(字节值);
themarketka

Answers:


107

我不确定我是否理解您的问题。

我刚刚尝试过,对于字节-12(有符号值),它返回整数244(等效于无符号字节值,但键入为int):

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

  public static void main(String[] args) {
    System.out.println(unsignedToBytes((byte) -12));
  }

这是你想做的吗?

Java不允许表达244的byte价值,将C.为了表达上述的正整数Byte.MAX_VALUE(127),你必须使用其他整型一样shortintlong


1
byte b = (byte)unsignedToBytes((byte) -12); 现在尝试打印b
Jigar Joshi

101
您为什么接受此作为正确答案?它所做的一切与您在问题中提到的方法完全相同-将字节转换为无符号整数。
Adamski

1
有时需要带符号的值,有时是无符号的,这一点很重要,因此这可能就是他接受此答案的原因。(byte)(b&0xff)没有任何意义,但是(byte)(Math.min((b&0xff)* 2,255))有意义,例如,在计算机图形学中,它只会使pixed表示为字节亮了两倍。:-)
iirekm

3
它可以被称为byteToUnsigned太
埃尔南Eche

193

使用Java对基元进行签名的事实与它们在内存/传输中的表示方式无关-字节仅为8位,以及是否将其解释为带符号范围取决于您自己。没有魔术标记说“此已签名”或“此未签名”。

当对原语进行签名时,Java编译器将阻止您将一个大于+127的值分配给一个字节(或小于-128的值)。但是,没有什么可以阻止您向下转换int(或简称)以实现此目的的:

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"
}

5
对于许多操作,它没有区别,但是对于某些操作,它却没有区别。无论哪种方式,您都可以将字节用作无符号,也可以使用无符号的char。
彼得·劳瑞

61
访问具有潜在负数的数组不是无关紧要的。
Stefan 2012年

3
@Stefan-我的意思是与它们在网络上的表示方式无关。
Adamski 2013年

5
这与问题无关。由于他提到需要将其传递给仅接受字节参数的函数,因此无论天气如何,我们都将其解释为独角兽的字节表示形式。Java将始终将其视为带符号的数字,例如,当此函数使用参数作为索引时,这可能会出现问题。但是,公平地说,我也否决了其他前2个答案,因为它们也不回答该问题。
Stefan 2013年

2
@Stefan为您+1。如果要使用字节访问256个元素的数组,则绝对相关。这是一个很好的例子,可以说明为什么每个人在转向Java或C#之前都应该开始学习C和C ++
Gianluca Ghettini 2015年

46

Java语言不提供任何类似unsigned关键字的内容。一个byte根据语言规范代表-128之间的值- 127。举例来说,如果byte被强制转换为intJava将解释第一位为标志和使用符号扩展

就是说,没有什么可以阻止您byte仅将8位查看并将这些位解释为0到255之间的值。请记住,您无能为力,无法将解释强加于其他人的方法。如果方法接受a byte,则该方法接受-128到127之间的值,除非另有明确说明。

为了方便起见,以下是一些有用的转换/操作:

往返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));

1
'char'不代表数字。
注销

26
简而言之:您错了
aioobe 2012年

36

Java中没有原始的无符号字节。通常的事情是将其转换为更大的类型:

int anUnsignedByte = (int) aSignedByte & 0xff;

是否需要强制转换为int?
NICH

它可以是隐式强制转换,但无论哪种方式都有强制转换。该演员确实签署了扩展名。这是一个问题。如果执行显式强制转换,至少可以看到这种情况正在发生。
foo



0

如果认为您正在寻找这样的东西。

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

0

Adamski提供了最佳答案,但答案并不完整,因此请阅读他的答复,因为它解释了我所未提供的详细信息。

如果您有一个系统函数需要将无符号字节传递给它,则可以传递一个有符号字节,因为它将自动将其视为无符号字节。

因此,如果系统功能需要四个字节(例如192 168 0 1作为无符号字节),则可以传递-64 -88 0 1,该功能仍将起作用,因为将它们传递给功能的操作将使它们无符号。

但是,尽管有些java.io读取方法返回int略过的字节,但由于系统功能隐藏在类之间以实现跨平台兼容性,因此您不太可能遇到此问题。

如果您希望看到这种效果,请尝试将带符号的字节写入文件,然后将其读回为无符号的字节。


1
没有诸如带符号或无符号字节之类的东西。
VlastimilOvčáčík2015年

您在示例中写入和读取字节的方式如何?
VlastimilOvčáčík2015年

0

你也可以:

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

说明:

比方说 a = (byte) 133;

在内存中,其存储为:“ 1000 0101”(十六进制的0x85)

因此其表示形式转换为 无符号 = 133,有符号 = -123(作为2的补码)

<< 24

当向左移动24位时,现在的结果是一个4字节的整数,表示为:

“ 10000101 00000000 00000000 00000000”(或“ 0x85000000”(十六进制))

然后我们有

(a << 24)>>> 24

并再次向右移动24位,但填充前导零。结果是:

“ 00000000 00000000 00000000 10000101”(或“ 0x00000085”(十六进制))

这是等于133的无符号表示形式。

如果您尝试强制转换,a = (int) a; 那么将会发生什么事,它将保留字节的2的补码表示形式并将其存储为int以及2的补码:

(int)“ 10000101” --->“ 11111111 11111111 11111111 10000101”

转换为:-123


2
在2019年,这是不必要的。只需使用java.lang.Byte.toUnsignedInt(byte value)。并且,如果您尚未使用Java 8,请尽快升级。Java 7和更早版本已经停产。
Stephen C

0

我正在尝试将此数据用作仅接受字节作为参数的Java函数的参数

这与接受要向其传递大于2 ^ 32-1的值的整数的函数没有太大区别。

听起来这取决于函数的定义和记录方式。我可以看到三种可能性:

  1. 它可能会明确证明该函数将字节视为无符号值,在这种情况下,该函数可能会执行您期望的操作,但似乎实现错误。对于整数情况,该函数可能会将参数声明为无符号整数,但对于字节情况则不可能。

  2. 它可能会证明此参数的值必须大于(或等于)零,在这种情况下,您滥用了该函数(传递了超出范围的参数),期望它做的比设计的要多。做。有了某种程度的调试支持,您可能会期望该函数引发异常或使声明失败。

  3. 文档可能什么也没说,在这种情况下,否定参数就是否定参数,其是否有意义取决于函数的功能。如果这没有意义,那么实际上应该将该函数定义/记录为(2)。如果这以非显而易见的方式有意义(例如,非负值用于索引数组,负值用于从数组末尾索引,因此-1表示最后一个元素),文档应说明其含义意味着,我希望这不是您想要它做的。


嗯,我想我刚刚发布了一个答复,该答复的目的是另一个有关字节的符号性的问题,但我想它在这里也还是有点相关……
Kevin Martin

-1

如果您有一个必须传递一个有符号字节的函数,那么如果传递一个无符号字节,您希望它做什么?

为什么不能使用任何其他数据类型?

通常,您可以使用一个字节作为无符号字节,进行简单转换或不进行转换。这完全取决于它的使用方式。您需要澄清自己打算做什么。


-1

尽管Java中没有在语言中包含无符号字节似乎令人讨厌(来自C),但实际上没什么大不了的,因为简单的“ b&0xFF”操作会在(稀有)中产生(有符号)字节b的无符号值。实际需要的情况。这些位实际上并没有改变-只是解释(这仅在例如对值进行一些数学运算时很重要)。


看别人的答案,您认为您的答案最佳/有用吗?
尽量

8
这并不罕见,因为您还没有碰到它。尝试实施协议,您将遇到一百万次。令人烦恼的是,我遇到过的大多数处理字节的用例,都想处理无符号字节(因为它们是字节,而不是数字)。疯狂的事情是,任何按位运算都会将其转换为int,这意味着扩展时任何“负”值都将是完全不同的值。是的,您可以通过始终屏蔽来解决它,但这是浪费时间,处理器,并且如果您忘记的话,会导致真正难以理解的错误。
Thor84no 2013年

我同意Thor84no:字节不是数字,也不应该带符号。另一方面,由于它们不是数字,我们甚至不应该拥有/使用+和-运算符。仅使用按位运算符可以很好地工作,另一方面,移位运算符不能像人们期望的那样工作,并且实际上java会将移位后的字节提升为int。
user1708042 2014年

1
@VlastimilOvčáčík在这种情况下,这几乎是不可能的,那是令人不快的事情。您可以x & 0xFF在需要的地方重复,也可以在任何地方重复behaveLikeAnUnsignedByte(x)。对于您使用字节值或字节数组(需要无符号)的每个位置,都需要这样做,没有避免这种重复的可行方法。您不能编写仅使用对字节变量的单个引用来读取和写入字节值的协议的实现。您的简单化观点也许可以解释为什么他们从来不愿意修复它。
Thor84no 2015年

-1

Java中没有无符号字节,但是如果您想显示一个字节,可以这样做,

int myInt = 144;

byte myByte = (byte) myInt;

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

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

输出:

myChar : 90

有关更多信息,请检查如何在Java中显示十六进制/字节值


无需自己定义。java.lang.Byte.toUnsignedInt(byte value);为此存在。
亚历山大-恢复莫妮卡

-2

根据Java中的限制,在当前数据类型格式中,几乎不可能实现无符号字节。您可以为实现的内容寻找其他语言的其他库,然后可以使用JNI对其进行调用。


我认为他不想将其存储为带符号的字节。他将其作为一个有符号字节接收,并且希望将其存储为一个int,这是完全有效的。他的问题是,无论从何处获取输入,它都将0到255之间的值表示为一个字节,但是Java将其解释为二进制补码带符号的值,因为Java不支持带符号的字节。
Zac

-2

是的,没有。我一直在研究这个问题。就像我明白这一点:

事实是Java已对-128到127之间的整数进行了签名。

public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

例如,如果您将-12个已签名的数字添加为未签名,则会得到244。但是您可以在已签名的状态下再次使用该数字,必须将其移回已签名的位置,并且它将再次为-12。

如果尝试将244添加到Java字节,则会得到outOfIndexException。

干杯..


3
无需自己定义。java.lang.Byte.toUnsignedInt(byte value);为此存在。
亚历山大-恢复莫妮卡

-3

如果要在Java中使用无符号字节,只需从您感兴趣的数字中减去256。它将产生带有负值的2的补码,这是所需的无符号字节数。

例:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

使用leJOSNXT程序块进行编程时,需要使用此类肮脏的技巧。


您确实意识到255的二进制值也是1111 1111,所以不需要减法,对吗?
尼克·怀特

@NickWhite,是的。但是Java使用2的补码,其中255不是11111111
XapaJIaMnu 2015年

对不起,但这是错误的。尝试一些实验。中的值speed_unsigned已签名。打印并查看。(- 256这里没有取得任何成就。)
Stephen C
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.