章
目
录
学习如何在Java中读取一个大小为GB级别的大型文件的所有行,并避免任何性能问题,例如内存使用过高或内存不足导致的OutOfMemoryError错误。
1.读取大型文件的策略
类似于XML文件的DOM解析器和SAX解析器,我们也可以使用两种方法来读取文件:
- 在处理之前将整个文件读入内存
- 逐行读取文件内容并独立处理每一行
第一种方法看起来更简洁,适用于内存需求非常低的较小文件(以千字节或少数几兆字节为单位)。如果用于读取大型文件,将很快导致数千兆字节文件的OutOfMemoryError错误。
第二种方法适用于读取数千兆字节的非常大的文件,当时将整个文件读入内存并不可行。在这种方法中,我们使用行流,即以流或迭代器的形式从文件中读取行。
本教程的重点是第二种方法的解决方案。
2. Java 8中的Files.lines() – 读取大型文件
使用Files.lines()方法,文件的 contents 被读取和处理,而且它是延迟计算的,所以在任何给定的时间,只有文件的一小部分存储在内存中。
这种方法的一个好处是我们可以直接编写Consumer操作,并使用Stream等较新的语言特性(如lambda表达式)。
Path filePath = Paths.get("C:/temp/file.txt")
//try-with-resources
try (Stream<String> lines = Files.lines( filePath ))
{
lines.forEach(System.out::println);
}
catch (IOException e)
{
e.printStackTrace();
}
3. IO的FileUtils.lineIterator()常见用法
lineIterator()使用一个Reader来迭代指定文件的行。使用try-with-resources语句在读取文件后自动关闭迭代器。
不要忘记将最新版本的的commons-io模块导入到项目的依赖中。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
File file = new File("C:/temp/file.txt");
try(LineIterator it = FileUtils.lineIterator(file, "UTF-8")) {
while (it.hasNext()) {
String line = it.nextLine();
// do something with line
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
4.阅读大型二进制文件
请注意,当我们通过流或逐行读取文件时,我们指的是字符型或文本文件。读取二进制文件时,UTF-8字符集可能会损坏数据,因此上述解决方案不适用于二进制数据文件。
要读取大型原始数据文件,例如电影或大型图像,我们可以使用Java NIO的ByteBuffer和FileChannel类。请记住,您需要尝试不同的缓冲区大小,并选择最适合您的缓冲区大小。
try (RandomAccessFile aFile = new RandomAccessFile("test.txt", "r");
FileChannel inChannel = aFile.getChannel();) {
//Buffer size is 1024
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (inChannel.read(buffer) > 0) {
buffer.flip();
for (int i = 0; i < buffer.limit(); i++) {
System.out.print((char) buffer.get());
}
buffer.clear(); // do something with the data and clear/compact it.
}
} catch (IOException e) {
e.printStackTrace();
}
5.结论
本Java教程讨论了应该使用哪些类来有效地读取大型文件。正确的解决方案取决于文件的类型和特定问题的其他决定因素。
我建议在您的环境中对所有解决方案进行基准测试,并根据它们的性能选择它们。