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

Posted

技术标签:

【中文标题】将 4 个字节转换为无符号 32 位整数并将其存储在 long【英文标题】:Convert 4 bytes to an unsigned 32-bit integer and storing it in a long 【发布时间】:2012-11-02 21:40:55 【问题描述】:

我正在尝试用 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:ing 仅在特定位置的 0xA5 中它 OR:s 在 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;

如果我想转换 0x67 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 位操作似乎“有点”多。(看看我在那里做了什么?)=)

【问题讨论】:

如果您使用的变量很长,那么 ALU 将始终在 64 位上执行操作。如果变量是 int,则 ALU 总是在 32 位上进行操作(而 ALU 功能的其他 32 位未使用)。对字节的操作很可能使 ALU 的 58 位未使用。这些操作总是在一个时钟周期内发生,所以说 10 位太多了。 不,您的工作实施正是正确的方法。 您不需要上面代码中的最后一个位和操作: value |= (bytes[3] 【参考方案1】:

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

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

不要纠结于位操作的数量,很可能编译器会将这些优化为字节操作。

此外,您不应该将long 用于 32 位值,只是为了避免符号,您可以使用 int 并忽略它大部分时间都已签名的事实。见this answer。

更新:需要将最高有效字节强制转换为 long,因为否则它的最高有效位将被移入 32 位整数的符号位,可能会使其为负数。

【讨论】:

需要使用 ((long)(bytes[+3] & 0xFF) 【参考方案2】:

你的想法是对的,我认为没有任何明显的改进。如果您查看java.io.DataInput.readInt spec,他们有相同的代码。他们切换了&lt;&lt;&amp; 的顺序,但其他都是标准的。

没有办法一次性从byte 数组中读取int,除非您使用内存映射区域,这方式 过大。

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

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

DataInputStream 工作的字节序与您使用的相反,因此您还需要一些 Integer.reverseBytes 调用。它不会更快,但更干净。

【讨论】:

以上是关于将 4 个字节转换为无符号 32 位整数并将其存储在 long的主要内容,如果未能解决你的问题,请参考以下文章

将整数拆分并存储为两个字节

将 8 个字节转换为有符号长整数(64 位)

int类型最大能存储到哪一位

将 256 位 AVX 向量存储为无符号长整数的最佳方法

如何从 32 位 R 整数中提取 4 位无符号整数?

将 2 个字节转换为有符号 16 位整数的正确方法是啥?