【问题标题】:Reading a binary file in Java vs C++在 Java 与 C++ 中读取二进制文件
【发布时间】:2011-08-07 20:41:12
【问题描述】:

我有一个需要快速读取的二进制文件(大约 100 MB)。在 C++ 中,我可以将文件加载到 char 指针中,然后通过递增指针来遍历它。这当然会非常快。

在 Java 中是否有相对快速的方法来做到这一点?

【问题讨论】:

  • 100 kB 非常小,不会导致任何语言的性能问题,除非您阅读了数千个这样的文件。
  • 我的意思是 100 mB...抱歉...绝对是一个有问题的错字

标签: java c++ performance file-io binary


【解决方案1】:

如果您使用内存映射文件或常规缓冲区,您将能够以硬件允许的速度读取数据。

File tmp = File.createTempFile("deleteme", "bin");
tmp.deleteOnExit();
int size = 1024 * 1024 * 1024;

long start0 = System.nanoTime();
FileChannel fc0 = new FileOutputStream(tmp).getChannel();
ByteBuffer bb = ByteBuffer.allocateDirect(32 * 1024).order(ByteOrder.nativeOrder());

for (int i = 0; i < size; i += bb.capacity()) {
    fc0.write(bb);
    bb.clear();
}
long time0 = System.nanoTime() - start0;
System.out.printf("Took %.3f ms to write %,d MB using ByteBuffer%n", time0 / 1e6, size / 1024 / 1024);

long start = System.nanoTime();
FileChannel fc = new FileInputStream(tmp).getChannel();
MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, size);
LongBuffer longBuffer = buffer.order(ByteOrder.nativeOrder()).asLongBuffer();
long total = 0; // used to prevent a micro-optimisation.
while (longBuffer.remaining() > 0)
    total += longBuffer.get();
fc.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f ms to read %,d MB MemoryMappedFile%n", time / 1e6, size / 1024 / 1024);

long start2 = System.nanoTime();
FileChannel fc2 = new FileInputStream(tmp).getChannel();
bb.clear();
while (fc2.read(bb) > 0) {
    while (bb.remaining() > 0)
        total += bb.get();
    bb.clear();
}
fc2.close();
long time2 = System.nanoTime() - start2;
System.out.printf("Took %.3f ms to read %,d MB File via NIO%n", time2 / 1e6, size / 1024 / 1024);

打印

Took 305.243 ms to write 1,024 MB using ByteBuffer
Took 286.404 ms to read 1,024 MB MemoryMappedFile
Took 155.598 ms to read 1,024 MB File via NIO

这是一个比你想要的大 10 倍的文件。之所以这么快,是因为数据被缓存在内存中(而且我有一个 SSD 驱动器)。如果您的硬件速度很快,则可以非常快地读取数据。

【讨论】:

    【解决方案2】:

    当然,您可以使用内存映射文件。

    这里有两个很好的示例代码链接:


    如果您不想走这条路,只需使用普通的InputStream(例如将DataInputStream包裹在BufferedInputStream中即可。

    【讨论】:

    • 如果它是二进制文件,正如 OP 所说,他肯定必须使用流,而不是阅读器。
    • 不,你比较一下告诉我们怎么样? :)
    【解决方案3】:

    大多数文件不需要内存映射,但可以通过标准 Java I/O 轻松读取,尤其是因为您的文件非常小。读取所述文件的合理方法是使用 BufferedInputStream。

    InputStream in = new BufferedInputStream(new FileInputStream("somefile.ext"));
    

    缓冲已经在 J​​ava 中针对大多数计算机进行了优化。如果你有一个更大的文件,比如 100MB,那么你会考虑进一步优化它。

    【讨论】:

      【解决方案4】:

      从磁盘读取文件将是最慢的部分,因此可能没有任何区别。当然,在这个单独的操作中 - JVM 仍然需要十年才能启动,所以加上那个时间。

      【讨论】:

        【解决方案5】:

        在此处查看这篇博客文章,了解如何在 Java 中将二进制文件读入字节数组:

        http://www.spartanjava.com/2008/read-a-file-into-a-byte-array/

        复制自链接:

        File file = new File("/somepath/myfile.ext");
        FileInputStream is = new FileInputStream(file);
        
        // Get the size of the file
        long length = file.length();
        
        if (length > Integer.MAX_VALUE) {
            throw new IOException("The file is too big");
        }
        
        // Create the byte array to hold the data
        byte[] bytes = new byte[(int)length];
        
        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
               && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
            offset += numRead;
        }
        
        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("The file was not completely read: "+file.getName());
        }
        
        // Close the input stream, all file contents are in the bytes variable
        is.close()    
        

        【讨论】:

        • (-1) 因为这个例子不满足要求:我们需要快速阅读。因此我们需要创建一个缓冲输入流。此代码将在字节磁盘之后读取文件字节。
        • @Andreas 他使用的任何方法都必须在某个时候从磁盘读取整个文件......它没有使用.read() 逐字节读取。它指定了文件的剩余量,以便在可能的情况下一次读取所有文件。相信我,这在 100kb 文件上会非常快,并给他一个字节数组,这样他就可以像在 C++ 中那样迭代它
        【解决方案6】:

        在这里使用 Java SDK 的 DataInputStream 会很有帮助。 DataInputStream 提供诸如 readByte() 或 readChar() 之类的函数,如果需要的话。 一个简单的例子可以是:

        DataInputStream dis = new DataInputStream(new FileInputStream("file.dat")); 
        try {
           while(true) {
              byte b = dis.readByte();
              //Do something with the byte
           } 
        } catch (EOFException eofe) {
        //Stream Ended
        } catch (IOException ioe) {
        //Input exception
        }
        

        希望对您有所帮助。当然,您可以将整个流读取到一个字节数组中并遍历它...

        【讨论】:

        • (-1) 因为这个例子不满足要求:我们需要快速阅读。因此我们需要创建一个缓冲的输入流。此代码将从磁盘中逐字节读取文件。
        猜你喜欢
        • 2012-07-08
        • 1970-01-01
        • 1970-01-01
        • 2017-10-01
        • 2011-09-03
        • 2016-01-15
        • 1970-01-01
        • 2015-07-27
        相关资源
        最近更新 更多