4

我正在尝试用 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 位操作似乎有点“有点”。(看看我在那里做了什么?)=)

4

2 回答 2

5

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

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并忽略它大部分时间都已签名的事实。看到这个答案

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

于 2013-03-03T09:25:06.097 回答
2

你的想法是对的,我认为没有任何明显的改进。如果您查看java.io.DataInput.readInt规范,他们有相同的代码。他们切换 和 的顺序<<&但在其他方面是标准的。

除非您使用内存映射区域,否则int无法从数组中一次性读取,这对于此来说是多余的。byte

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

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

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

于 2012-11-02T22:01:33.300 回答