将4个字节转换为无符号的32位整数并将其存储为long

我正在尝试用Java读取二进制文件。 我需要读取无符号8位值,无符号16位值和无符号32位值的方法。 这样做的最好(最快,最好看的代码)是什么? 我用c ++完成了这个并做了类似这样的事情:

uint8_t *buffer; uint32_t value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24; 

但是在Java中,如果例如buffer [1]包含一个设置了符号位的值,则会导致问题,因为左移的结果是int(?)。 而不是OR:在特定的位置只有0xA5,或者在0xFFFFA500或类似的东西中,这会“损坏”两个顶部字节。

我现在有一个代码,看起来像这样:

 public long getUInt32() throws EOFException, IOException { byte[] bytes = getBytes(4); long value = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); return value & 0x00000000FFFFFFFFL; } 

如果我想转换四个字节0​​x67 0xA5 0x72 0x50,结果是0xFFFFA567而不是0x5072A567。

编辑:这很棒:

 public long getUInt32() throws EOFException, IOException { byte[] bytes = getBytes(4); long value = bytes[0] & 0xFF; value |= (bytes[1] << 8) & 0xFFFF; value |= (bytes[2] << 16) & 0xFFFFFF; value |= (bytes[3] << 24) & 0xFFFFFFFF; return value; } 

但是,有没有更好的方法来做到这一点? 对于像这样的简单事情,10位操作似乎“有点”……(看看我在那里做了什么?)=)

你有正确的想法,我认为没有任何明显的改善。 如果你看一下java.io.DataInput.readInt规范 ,它们就有相同的代码。 它们切换<<&的顺序,但是否则是标准的。

除非你使用内存映射区域,否则无法从byte数组中一次读取一个int ,这对此来说太过分了。

当然,您可以直接使用DataInputStream而不是先读入byte[]

 DataInputStream d = new DataInputStream(new FileInputStream("myfile")); d.readInt(); 

DataInputStream工作方式与您使用的相反,因此您还需要一些Integer.reverseBytes调用。 它不会更快,但更清洁。

示例代码的问题在于,当您从字节隐式转换为long时,使用符号扩展进行转换,这意味着如果字节的第一位为1,则使用1而不是零填充long。 通过使用转换为long来阻止符号扩展,您的代码可以完美地运行。

 public static long byteAsULong(byte b) { return ((long)b) & 0x00000000000000FFL; } public static long getUInt32(byte[] bytes) { long value = byteAsULong(bytes[0]) | (byteAsULong(bytes[1]) << 8) | (byteAsULong(bytes[2]) << 16) | (byteAsULong(bytes[3]) << 24); return value; } 

如果您小心,可以使用带符号的值来包含位。 您需要避免的是任何forms或签名操作,例如算术和有符号位移。 如果您需要将值打印为数字,请意识到所有内置的java方法都会导致大的无符号数字显示为负数。

最重要的是要了解所有这些,关于位移。 向右移动时, >>操作符将保持数字的符号为二。 这意味着如果最左边的位是1,则移入的位将是1而不是0。 好消息是Java至少有一个无符号位移位运算符,它总是以零移位,它是>>> 。 例:

 int bits; bits >>> 4; 

永远记住,一堆位表达的数据是任意的。 尽管Java的内部方法都将这些位视为两个恭维,但如果您不使用它们中的任何一个,则有符号字节包含您放入它们的完全相同的位。

更常规的版本首先将字节转换为无符号值作为整数:

 public long getUInt32() throws EOFException, IOException { byte[] bytes = getBytes(4); long value = ((bytes[0] & 0xFF) << 0) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 24); return value; } 

不要挂断位操作的数量,很可能编译器会优化那些字节操作。

另外,为了避免使用符号,你不应该使用long来表示32位值,你可以使用int并忽略它在大多数时候都被签名的事实。 看到这个答案 。