我正在开发一个已经用Python开发的Android应用程序。在Python程序中,有一行我想要完全理解:
self.comfd = Serial(...) # from the pySerial API
....
self.buffer = list(struct.unpack('192H', self.comfd.read(384)))
根据我的理解,self.comfd.read(384)
正在读取384个字节,而unpack('192H'
正在从该数据中解包192个无符号短路。这是对的吗?
现在在Java中,我已经能够使用
读取缓冲区了SerialPort device = SerialPort(file, baud, flags);
InputStream in = device.getInputStream();
我的问题是,现在我有输入流,如何创建像Python程序一样的无符号短路?
我尝试了什么(没有产生正确的值):
byte[] buffer = new byte[384];
in.read(buffer);
ByteBuffer bb = ByteBuffer.allocate(2);
for (int i = 0; i < buffer.length / 2; i++) {
bb.put(buffer[i]);
bb.put(buffer[i + 1]);
short val = bb.getShort(0);
System.out.println(val);
bb.clear();
}
我做错了什么?谢谢你的帮助!
编辑:我收录了Jason C的答案,而且我的循环错误。通过将其更改为
for (int i = 0; i < buffer.length; i=i+2)
解决了我的问题。
答案 0 :(得分:3)
您可以使用char(它是Java中的16位无符号值),例如:
byte[] buffer = ...;
ByteBuffer bb = ByteBuffer.wrap(buffer); // don't need to put()
int val = (int)bb.getChar(0);
使用bb.order()设置big-little-endian。
你也可以在不使用ByteBuffer的情况下将2个字节打包成int(假设是little-endian)。字节是用Java签名的,所以你必须在移位之前将字节转换为无符号值,你可以通过将它临时存储在一个short(或int或任何大到足以容纳0-255的东西)来完成:
short b0 = (buffer[0] & 255); // trick converts to unsigned
short b1 = (buffer[1] & 255);
int val = b0 | (b1 << 8);
// or just put it all inline:
int val = (buffer[0]&255) | ((buffer[1]&255) << 8);
对于big-endian数据,只需交换b0和b1。
希望有所帮助。
答案 1 :(得分:1)
Java没有无符号数字(char
是无符号的16位但它不是数字,char
的数学将始终导致int
的隐式强制转换
如果您将2个字节的无符号数据读入short
并想要查看范围为0-65535(而不是-32768 - 32767)的值,则必须使用可以包含值的类型那范围。
如果是16位short
,则下一个更大的是32位int
。实现这一诀窍的转换是
short signed = ...;
int unsigned = signed & 0xFFFF;
假设signed
的值为0xFFFF
,则会发生以下情况:
short signed = -1; // FFFF on byte level
表达式signed & 0xFFFF
包含short
和int
。 0xFFFF
是一个文字整数类型号,在Java源代码中找到它时被视为int
。您可以将其long
更改为0xFFFFL
(如果您想将未签名的int
转换为long
,则需要这样做。)
由于&
运算符需要共同类型的Java,因此Java会默默转换较小的一行。
int stillSigned = (int) signed; // hidden step
它仍将具有完全相同的-1值,因为这是在无条件查看它之前的情况,但它在字节级别上更改为0xFFFFFFFF
。
现在应用位操作来删除所有添加的FF
s
int unsigned = stillSigned & 0xFFFF;
并且您在字节级别最终得到0x0000FFFF
,并且最终可以看到65535的值。
由于您碰巧拥有16位值,因此可以使用char
并将其强制转换为int
。
char value = ...;
int unsigned = value;
但上述方法适用于任何未签名的转化:byteValue & 0xFF
,shortValue & 0xFFFF
,intValue & 0xFFFFFFFFL
接下来你应该做的就是不要使用简单的InputStream
来做
SerialPort device = SerialPort(file, baud, flags);
InputStream in = device.getInputStream();
byte[] buffer = new byte[384];
in.read(buffer);
原因是InputStream#read(byte[])无法保证读取缓冲区中所需的所有字节。它返回它已读取的字节数,如果流已完成,则返回-1
。手动编写确保填充缓冲区的代码是令人讨厌的,但有一个简单的解决方案:DataInputStream
SerialPort device = SerialPort(file, baud, flags);
DataInputStream in = new DataInputStream(device.getInputStream());
byte[] buffer = new byte[384];
in.readFully(buffer);
DataInputStream
具有非常好的功能,您可以使用:
SerialPort device = SerialPort(file, baud, flags);
DataInputStream in = new DataInputStream(device.getInputStream());
int unsignedShort = in.readUnsignedShort();
从byte[]
数据中获取不同数字的另一种方法是使用ByteBuffer
,因为它提供了.getShort()
等方法
SerialPort device = SerialPort(file, baud, flags);
DataInputStream in = new DataInputStream(device.getInputStream());
byte[] buffer = new byte[384];
in.readFully(buffer);
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
while (byteBuffer.hasRemaining()) {
int unsigned = byteBuffer.getChar();
System.out.println(unsigned);
}