-1

我正在移植一个已经用 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)这解决了我的问题。

4

2 回答 2

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 中是有符号的,因此您必须在移位之前将字节转换为无符号值,您可以通过将其临时存储在一个短字节(或一个整数或任何大到足以容纳 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);

对于大端数据,只需交换 b0 和 b1。

希望有帮助。

于 2013-08-06T17:20:13.320 回答
1

Java 没有无符号数(char是 16 位无符号数,但它不是数字,并且数学char总是会导致隐式转换为int

如果您将 2 个字节的无符号数据读入 ashort并希望查看 0-65535(而不是 -32768 - 32767)范围内的值,则必须使用可以具有该范围内的值的类型。

在 16bit 的情况下,short下一个更大的是 32bit int。成功的转换是

short signed = ...;
int unsigned = signed & 0xFFFF;

假设signed有这个值0xFFFF会发生什么:

short signed = -1; // FFFF on byte level

表达式signed & 0xFFFF包含 ashort和 a int0xFFFF是一个字面整数类型数,当在 Java 源代码中找到它时会被视为int. 您可以long通过将其更改为来实现0xFFFFL(如果您想将 unsigned 转换为 ,则需要这样做intlong

由于&运算符需要双方都在一个通用类型中,Java 会默默地转换较小的类型。

int stillSigned = (int) signed; // hidden step

它仍将具有完全相同的值 -1,因为在查看它时它是无符号的,但它在 bytelevel 上更改为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);
}
于 2013-08-06T19:00:41.000 回答