【问题标题】:Java read binary file (unsigned long long)Java读取二进制文件(unsigned long long)
【发布时间】:2013-11-21 12:56:28
【问题描述】:

我想将 C 代码转换为 Java。它读取一个二进制文件:

int main(int argc, char**argv)
{
    FILE *fd;
    unsigned long trameNumber = 0;
    unsigned long long INDEX;

    fd = fopen(argv[1],"rb");
    if (fd == NULL)
    {
        printf("Usage %s [File]\n", argv[0]);
        exit(1);
    }

    fread(&INDEX, sizeof(INDEX),1, fd);
    printf("INDEX %llx\n",INDEX);
    trameNumber++;

    while (fread(&INDEX, sizeof(INDEX),1, fd) != 0)
    {
        printf("INDEX %llx\n",INDEX);
        trameNumber++;
    }

    fclose(fd);
    printf("%lu", trameNumber);

    return 0;
}

此代码的输出如下所示:

INDEX 0
INDEX 9800000000000000
INDEX 1801000000000000
INDEX 5001000000000000
INDEX b801000000000000

这是我的 Java 代码。我已经尝试使用BigInteger

public static final int DATA_BYTE_LENGHT = 8;

public void readBinary(final String readFilePath)
{
    // A 8 byte buffer = 64 bits
    ByteBuffer byteBuffer = ByteBuffer.allocate(DATA_BYTE_LENGHT);

    // Those channels will be used to read/write files
    FileChannel channelFileInput = null;

    BigInteger bigIndex = null;

    try {

        // File to read
        final File fileRead = new File(readFilePath);

        // Channel used to read the file.
        channelFileInput = new FileInputStream(fileRead).getChannel();

        byteBuffer.put(new byte[DATA_BYTE_LENGHT]);
        byteBuffer.rewind();

        // While the file has content
        while( channelFileInput.read(byteBuffer) != -1 ) {

            byteBuffer.rewind();

            // Big integer positive
            bigIndex = new BigInteger(1, byteBuffer.array());

            byteBuffer.rewind();

            System.out.println("INDEX "+bigIndex.toString(16));

            // Clear the buffer
            byteBuffer.put(new byte[DATA_BYTE_LENGHT]);
            byteBuffer.rewind();

        }

    } catch(FileNotFoundException e) {
        System.err.println("The file cannot be read: "+e.getMessage());
    } catch(Exception e) {
        System.err.println(e.getMessage());
    } finally {
        // Close file connections
        IOUtils.closeQuietly(channelFileInput);
    }
}

但是,read() 似乎没有正确读取文件。因为输出是:

INDEX 0
INDEX 98
INDEX 118
INDEX 150
INDEX 1b8

这可能是字节序问题吗?如何解决?

谢谢

【问题讨论】:

    标签: java c++ binaryfiles endianness


    【解决方案1】:

    BigInteger 构造函数采用 big-endian 表示,而文件中的数据似乎以 little-endian 存储。要解决此问题,您可以反转您获得的数组中的字节,或使用ByteBuffer 中的order 方法设置字节顺序并使用long 数据类型:

    // before loop
    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
    
    // in loop
    long bigIndex = byteBuffer.getLong();
    byteBuffer.rewind();
    System.out.println("INDEX "+Long.toHexString(bigIndex));
    

    Java 的 long 数据类型是有符号的。这可能是也可能不是问题,具体取决于您以后要对这些数字做什么。

    更新:如果你必须使用BigInteger,你可以像我之前说的那样反转字节数组,或者使用long读取数字,然后更正符号:

    BigInteger bi = BigInteger.valueOf(bigIndex & ~Long.MIN_VALUE);
    if (bigIndex < 0) bi = bi.setBit(63);
    

    【讨论】:

    • 是的,我用order() 尝试了这个解决方案,但我得到了相同的结果。正如您在我的问题(C++ 代码)中看到的那样,不幸的是,我的数字没有签名。
    • 有符号数和无符号数仅在比较和除法等操作中处理最高有效位的方式不同,对于大多数目的,无论您使用有符号还是无符号都没有区别。 order() 方法不会影响array() 的行为,您需要使用getLong() 和类似的方法来查看它的区别。
    • 好吧,我不知道我必须将其转换为 long 才能使用 order()。谢谢
    • 构造函数BigInteger(long) 不存在。 See here。但我理解这个想法。我可以使用new BigInteger(Long.toHexString(bigIndex), 16)
    • 没错,要将long 转换为BigInteger,你必须使用valueOf method,我应该在编辑之前检查一下。像您一样使用中间十六进制字符串同样有效,并且可能对大多数开发人员来说更容易理解。
    【解决方案2】:

    Java 将所有原始数据类型定义为使用大端序。如果您在 x86(windows 或 linux 或 OSX)平台上工作,您的计算机可能使用 little endian。 Endianess 可能是你痛苦的原因。您可能可以使用掩码和移位操作来反转字节顺序来解决问题。这个问题的答案其实在this answer

    【讨论】:

    • 感谢您的链接。作为记录,Apache Commons IO 提供了一个非常好的工具来消除字节序问题:EndianUtils
    猜你喜欢
    • 2017-05-18
    • 2013-03-05
    • 2012-08-21
    • 2016-07-29
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多