Java读取第n行

5

我想从一个文本文件中读取特定的一行,但是我不想把文件加载到内存中(它可能会非常大)。

我一直在寻找,但每个例子都需要读取每一行(这会减慢我的代码,因为有超过100,000行),或者将整个文件加载到数组中并获取正确的元素(文件将有很多行输入)。

我想要实现的示例:

String line = File.getLine(5);

“code”并非真实的代码,它只是用来展示我想要表达的原理。

有没有办法做到这一点?

-----编辑-----

我刚刚意识到在读取行时也会写入文件(将内容添加到文件末尾)。


2
阅读一百万行文件的每一行都是非常快速的操作,你认为这有什么问题吗? - Keppil
1
skip这样的东西怎么样?但是是的,你需要一个固定的行长度或每一行的行起始位置,或者至少知道每一行的长度和唯一的行ID(在这种情况下,如果超出范围,则可能需要使用Random Access File)。 - Bernhard Barker
尝试使用RandomAccessFile中的seek(long pos)方法,然后使用readLine()获取数据。 - VKPRO
你的访问模式是什么?你是读取第 n 行并不再使用该文件,还是按顺序读取行(始终递增n而不递减),还是随机读取该文件中的多个行? - rob
我需要的是两个函数:1)读取用户选择的行,2)读取自上次读取以来添加的新行。我计划每10秒读取一次文件。我已经得到了行数,并可以找出新行号,但我需要读取那些行。 - NoLiver92
@NoLiver92,我觉得你需要从行号映射到偏移量来解决使用情况1,并且你只需要保存最近的偏移量(而不是最近的行号)来解决使用情况2。https://dev59.com/_XHYa4cB1Zd3GeqPKlwv#16206497 - rob
8个回答

12

有没有办法做到这一点?

如果每行的字节数是固定的,那么就可以做到,否则不行。

你不必实际上在内存中保留每一行 - 但你必须阅读整个文件才能找到想要的那一行,否则你就不知道从哪里开始读取。


哦,好吧,我猜我得逐行阅读它,除非像Rob在下面说的那样有效。它仍然是逐行阅读,但你可以映射每一行的偏移量,这样你就可以将指针移动到你想要的位置。 - NoLiver92

4

您必须逐行阅读文件,否则您怎么知道您已经到达第5行(就像您的示例中一样)?

编辑:

如果您知道每行有多少字节,则可能还需要查看Random Access Files,这可能会有所帮助,正如Jon Skeet所说。


2
没错,即使使用随机访问文件,您也无法按行读取--只能按字节读取。重要的是要记住,行(在文件系统上的文件中)是通过换行符任意标记的。一个1KB的文件可能有1024行,也可能只有一行。 - darkpbj
@darkpbj RandomAccessFile有一个readLine()方法,所以我认为你的意思是RandomAccessFile没有按行查找的能力。 - rob

3
使用BufferedReader(http://docs.oracle.com/javase/1.5.0/docs/api/java/io/BufferedReader.html)是最简单的方法,因为您可以指定缓冲区大小。您可以这样做: BufferedReader in = new BufferedReader(new FileReader("foo.in"), 1024);
in.readLine();
in.readLine();
in.readLine();
in.readLine();
String line = in.readLine();

2

1) 读取用户选择的一行,

如果你只需要偶尔读取用户选择的一行(或者文件足够小),那么你只需要从文件开头逐行读取,直到找到所选行。

如果你需要频繁地读取用户选择的一行,那么你应该建立一个行号和偏移量的索引。例如,第42行对应文件中2347字节的偏移量。理想情况下,你只需要读取整个文件一次并存储索引,例如在一个map中使用行号作为键,偏移量作为值。

2) 读取自上次读取以来新增加的行。我计划每10秒读取一次文件。我已经得到了行数,并且可以找到新的行号,但我需要读取这些行。

对于第二点,你可以简单地保存当前文件指针的偏移量而不是保存当前行号,但如果继续建立索引仍然能够提供显著的性能优势,那么继续建立索引也无妨。

  1. 使用RandomAccessFile.seek(long offset)将文件指针设置为最近保存的偏移量(首先确认文件长度大于最近保存的偏移量,如果不是,则没有添加任何内容)。
  2. 使用RandomAccessFile.readLine()读取文件的一行。
  3. 在读取完行后,调用RandomAccessFile.getFilePointer()获取当前偏移量,并可选地将(currLineNo+1,offset)放入索引中。
  4. 重复步骤2-3直到到达文件结尾。

但是不要过度关注性能优化,除非性能已经成为问题或者高度可能成为问题。


这看起来不错,我今晚回家后会试一下(今天必须去工作),谢谢。 - NoLiver92
有人有这种方法的片段吗? - jechaviz

1
对于小文件:
String line = Files.readAllLines(Paths.get("file.txt")).get(n);

对于大文件:
String line;
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line = lines.skip(n).findFirst().get();
}

Java 7:

String line;
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    for (int i = 0; i < n; i++)
        br.readLine();
    line = br.readLine();
}

来源: 从文件中读取第n行


0

这里是我写的一段代码片段,它可以读取文件并将每10行(包括第一行)写入新文件(writer)。你可以用任何你想要的内容替换try部分。如果要更改要读取的行数,只需将if语句中的“0”更改为你想要读取的行数即可。“lc.endsWith("0")”。但是,如果在读取文件时正在写入文件,那么此代码仅适用于运行此代码时文件中包含的数据。

            LineNumberReader  lnr = new LineNumberReader(new FileReader(new File(file)));
            lnr.skip(Long.MAX_VALUE);
            int linecount=lnr.getLineNumber();
            lnr.close();

        for (int i=0; i<=linecount; i++){

            //read lines
            String line = bufferedReader.readLine();
            String lc = String.valueOf(i);

            if (lc.endsWith("0")){

                try{

                    writer.append(line+"\n");
                    writer.flush();

                    }catch(Exception ee){
                }
            }
        }

  1. 这会读取每一行
  2. lc.endsWith("0") 可以替换为 ((i % 10) == 0)
- Philip Whitehouse

0
唯一的方法是建立每行所在位置的索引(只需要记录每行的结尾),没有办法根据从开头的索引随机访问一行,你必须在该行之前读取每个字节。
顺便说一下:在快速计算机上读取100,000行可能只需要一秒钟。

0
如果性能是一个重要的问题,并且您经常从静态文件中读取随机行,则可以通过阅读文件并构建索引(基本上只是一个long[]),来优化这个问题。
一旦您拥有了这个索引,您就知道在文件中跳转到哪里,然后您可以读取到下一个换行符以检索完整的行。

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