读取文本文件时跳过x最后一行

时间:2011-12-13 18:44:35

标签: java io

我逐行从大文件中读取文本数据 但我需要读取n-x行(不读最后x行)。

如果不读取整个文件超过1次怎么办呢? (我读行并立即处理它,所以我不能回去)

3 个答案:

答案 0 :(得分:6)

您需要使用简单的预读逻辑。

首先读取x行并将它们放入缓冲区。然后,您可以一次重复读取一行,将其添加到缓冲区的末尾,并处理缓冲区中的第一行。当您到达EOF时,缓冲区中有x条未处理的行。

更新:我注意到有关问题的评论和我自己的答案,所以只是为了澄清:我的建议在n未知时有效。当然应该知道x。您需要做的就是创建一个简单的缓冲区,然后用x行填充缓冲区,然后开始处理。

关于缓冲区的实现,只要我们讨论Java的内置集合,就可以使用简单的LinkedList。因为你要在缓冲区中为你放入的每一行拉出一行,ArrayList对数组索引的不断移位不会很好。一般来说,支持数组的缓冲区必须是循环的,以避免性能不佳。

答案 1 :(得分:6)

在这篇文章中,我将为您提供两种完全不同的方法来解决您的问题,并且根据您的使用情况,其中一种解决方案将比另一种更适合。

备选方案#1

这种方法虽然非常复杂,但内存效率很高,如果您要跳过大量内容,建议使用此方法,因为在处理过程中,您只会在内存中一次存储一行。

这篇文章中的实现可能不是超级优化,但其背后的理论很明确。

您将首先向后读取文件,搜索N个换行符。如果您已成功找到文件中的某个位置,您希望稍后停止处理,则会跳回文件的开头。

备选方案#2

这种方法易于理解且非常直接。在执行期间,您将在内存中存储N行数,其中N是您最后要跳过的行数。

这些行将存储在FIFO容器中(先进先出)。您将最后一条读取行附加到FIFO,然后删除并处理第一个条目。这样,您将始终处理距离文件末尾至少N个条目的行。



备选方案#1

这可能听起来很奇怪,但它绝对可行,而且我建议你这样做的方式;首先阅读文件向后

  1. 寻找文件的末尾
  2. 读取(并丢弃)字节(朝向文件开头),直到找到SKIP_N换行符
  3. 保存此职位
  4. 寻找文件的开头
  5. 读取(并处理)行,直到您到达已存储的位置

  6. 示例代码:

    以下代码将从42中删除最后/tmp/sample_file行,并使用本文前面所述的方法打印其余行。

    import java.io.RandomAccessFile;
    import java.io.File;
    
    import java.lang.Math;
    
    public class Example {
      protected static final int SKIP_N = 42;
    
      public static void main (String[] args)
        throws Exception
      {
        File fileHandle            = new File ("/tmp/sample_file");
        RandomAccessFile rafHandle = new RandomAccessFile (fileHandle, "r");
        String s1                  = new String ();
    
        long currentOffset = 0;
        long endOffset     = findEndOffset (SKIP_N, rafHandle);
    
        rafHandle.seek (0);
    
        while ((s1 = rafHandle.readLine ()) != null) {
          ;   currentOffset += s1.length () + 1; // (s1 + "\n").length
          if (currentOffset >= endOffset)
            break;
    
          System.out.println (s1);
        }
      }
    
      protected static long findEndOffset (int skipNLines, RandomAccessFile rafHandle)
        throws Exception
      {
        long currentOffset = rafHandle.length ();
        long endOffset     =  0;
        int  foundLines    =  0;
    
        byte [] buffer      = new byte[
          1024 > rafHandle.length () ? (int) rafHandle.length () : 1024
        ];
    
        while (foundLines < skipNLines && currentOffset != 0) {
          currentOffset = Math.max (currentOffset - buffer.length, 0);
    
          rafHandle.seek      (currentOffset);
          rafHandle.readFully (buffer);
    
          for (int i = buffer.length - 1; i > -1; --i) {
            if (buffer[i] == '\n') {
              ++foundLines;
    
              if (foundLines == skipNLines)
                endOffset = currentOffset + i - 1; // we want the end to be BEFORE the newline
            }
          }
        } 
    
        return endOffset;
      }
    }
    


    备选方案#2

    1. 逐行从您的文件中读取
    2. 在每个成功阅读的行上,在LinkedList<String>
    3. 的背面插入一行
    4. 如果您的LinkedList<String>包含的行数多于您要跳过的行数,请删除第一个条目并对其进行处理
    5. 重复直到没有更多行要阅读

    6. 示例代码

      import java.io.InputStreamReader;
      import java.io.FileInputStream;
      import java.io.DataInputStream;
      import java.io.BufferedReader;
      
      import java.util.LinkedList;
      
      public class Example {
        protected static final int SKIP_N = 42; 
      
        public static void main (String[] args)
          throws Exception
        {
          String line;
      
          LinkedList<String> lli = new LinkedList<String> (); 
      
          FileInputStream   fis = new FileInputStream   ("/tmp/sample_file");
          DataInputStream   dis = new DataInputStream   (fis);
          InputStreamReader isr = new InputStreamReader (dis);
          BufferedReader    bre = new BufferedReader    (isr);
      
          while ((line = bre.readLine ()) != null) {
            lli.addLast (line);
      
            if (lli.size () > SKIP_N) {
              System.out.println (lli.removeFirst ());
            }   
          }   
      
          dis.close (); 
        }
      }
      

答案 2 :(得分:2)

请阅读前面的x行。这是一个x行的队列。