如何从一个巨大的文件中读取任意但连续的n行

时间:2012-02-07 09:35:16

标签: java

我想读任意数行。这些文件暂时是正常的ascii文本文件(以后可能是UTF8 /多字节文件)

所以我想要的是一个方法只读取特定行的文件(例如101-200),这样做时它不应该阻止任何事情(即同一个文件可以被另一个线程读取201- 210,它不应该等待第一次读取操作。

如果没有要阅读的行,它应该优雅地返回它可以读取的内容。方法的输出可以是List

到目前为止我想到的解决方案是首先读取整个文件以查找行数以及每个新行字符的字节位置。然后使用RandomAccessFile读取字节并将它们转换为行。我必须将字节转换为字符串(但这可以在读取完成后完成)。我会通过适当的簿记来避免文件异常结束以便读取文件。解决方案效率有点低,因为它经历了两次文件,但文件大小可能非常大,我们希望在内存中保留很少。

如果有一个可以工作的库,但更简单的原生java解决方案会很棒。

我一如既往地感谢您的澄清问题,我会在此过程中编辑此问题。

2 个答案:

答案 0 :(得分:0)

为什么不使用Scanner并直接遍历hasNextLine()直到达到你想要的数量,然后根据你的意愿获取尽可能多的行...如果它用完了,它将优雅地失败。那样你只需要读一次文件(除非Scanner完全读完它......我从来没有看过它......但听起来并不像你在乎,所以......你去了:)< / p>

答案 1 :(得分:0)

如果你想减少内存消耗,我会使用内存映射文件。这几乎不使用堆。保存在内存中的文件数量由操作系统处理,因此您无需自行调整行为。

FileChannel fc = new FileInputStream(fileName).getChannel();
final MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());

如果您有2 GB或更多的文件,则需要多个映射。在最简单的情况下,您可以扫描数据并记住所有索引。它们自己的索引可能占用大量空间,因此您可能只记得每个Nth,例如每十分之一。

e.g。一个包含40字节行的2 GB文件可能有5000万行需要400 MB内存。

拥有大索引的另一种方法是创建另一个内存映射文件。

FileChannel fc = new RandomAccessFile(fileName).getChannel();
final MappedByteBuffer map2 = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size()/10);

问题是,在开始之前,您不知道文件需要多大。幸运的是,如果你把它设置得比需要的大,它就不会消耗内存或磁盘空间,所以最简单的事情就是把它做得非常大,当你知道它需要的大小时就截断它。

这也可用于避免每次加载文件时重新索引文件(仅在更改时)如果文件仅附加到,则可以每次从文件末尾开始索引。

注意:使用这种方法可以使用大量的虚拟内存,对于64位JVM,这没有问题,因为你的限制很可能是256 TB。对于32位应用程序,根据您的操作系统,您的限制可能是1.5 - 3.5 GB。