Java和C ++中读取二进制文件的区别

7

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

在Java中是否有类似的快速方法?


6
100 kB非常小,在任何语言中都不应该引起性能问题,除非你读取成千上万个这样的文件。 - JB Nizet
我是说100 MB...抱歉...这绝对是一个有问题的打字错误。 - poy
6个回答

8

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

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硬盘)。如果您拥有快速的硬件,数据可以被读取得很快。


6

1
如果这是一个二进制文件,正如OP所说,他肯定必须使用流而不是读取器。 - JB Nizet
不,你怎么比较然后告诉我们呢? :) - aioobe

1

大多数文件不需要内存映射,而可以通过标准Java I/O进行读取,特别是针对如此小的文件。使用BufferedInputStream是读取这些文件的合理方式。

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

对于大多数计算机,Java已经对缓冲进行了优化。如果您有一个更大的文件,比如100MB,那么您可以考虑进一步优化。


0

在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
由于这个例子不能满足要求,所以返回值为-1。因此我们需要创建一个缓冲输入流来快速读取。这段代码将从磁盘逐字节读取文件。 - Andreas Dolk
@Andreas 无论他使用什么方法,都必须在某个时候从磁盘上读取整个文件... 它不是使用 .read() 逐字节读取。它是指定剩余的文件大小,以便尽可能一次性读取。相信我,在一个100kb的文件上,这将非常快,并给他一个字节数组,以便他可以像在C++中一样迭代它。 - Paul

0

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


-1

在编程中,使用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。因此,我们需要创建一个缓冲输入流来快速读取。这段代码将从磁盘逐字节读取文件。 - Andreas Dolk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接