读取包含无符号数的二进制流

Read binary stream containing unsigned numbers

我想读取包含 32 位无符号整数和 8 位无符号整数的二进制文件。我已经知道 DataInputStream 但它的方法 readInt returns 有符号整数并且没有读取 unsigned 整数的方法(有这样的方法用于 16-位和 8 位整数)。

读取单独的字节并将它们按位连接是“官方”方法吗?将字节读入 ByteArray 并使用移位和按位 or 将它们组合成整数会显着降低性能吗?

你可以使用

long value = Integer.toUnsignedLong​(dataInputStream.readInt());

这相当于 Java 8 之前的代码

long value = dataInputStream.readInt() & 0xFFFFFFFFL;

重点是signed和unsigned只是bit pattern的不同解释,但是要读取四个字节的数量,readInt()总是足够的。上面的操作转换为有符号long,一种能够覆盖unsigned int.

所有值的数据类型

但由于 int 确实已经包含所有信息,因此无需立即将其转换为 long。用于表示有符号数的 Two’s Complement 甚至允许执行基本操作,即 +-*,而不区分有符号数和无符号数。对于其他操作,Java 8 引入了通过将 int 值解释为无符号来执行它们的方法:

我遇到的一个实际例子是解析class个文件。这些文件的大小在某些地方编码为 un​​signed int,但对于大多数标准 Java API,class 文件作为字节数组或 ByteBuffer 实例交付,其中不能包含超过 2³¹ 字节.因此,处理更大的数字对于无论如何都不可能正确的事情来说是不必要的复杂化,因为必须截断包含如此大尺寸规格的 class 文件。

所以处理这个问题的代码基本上是这样的:

int size = input.readInt();
if(Integer.compareUnsigned(size, Integer.MAX_VALUE)>0) throw new IllegalArgumentException(
    "truncated class file (attribute size "+Integer.toUnsignedString(size)+')');
// just use the int value

或没有 Java 8 个特征
(更简单,只要 reader 理解 Two’s Complement):

int size = input.readInt();
if(size < 0) throw new IllegalArgumentException(
    "truncated class file (attribute size "+(size&0xFFFFFFFFL)+')');
// just use the int value

(另见 this answer