Пропустить х последних строк при чтении текстового файла - PullRequest
4 голосов
/ 13 декабря 2011

Строка за строкой я читаю текстовые данные из большого файла.
Но мне нужно читать только nx строк (не читать последние x строк).

Как это сделать без чтения всего файлаболее 1 раза?
(я прочитал строку и сразу обработал ее, поэтому не могу вернуться)

Ответы [ 3 ]

6 голосов
/ 13 декабря 2011

Вам нужно использовать простую логику упреждающего чтения.

Сначала прочитайте x строки и поместите их в буфер. Затем вы можете многократно читать по одной строке за раз, добавлять ее в конец буфера и обрабатывать первую строку в буфере. Когда вы достигнете EOF, у вас будет x необработанных строк в буфере.

Обновление: Я заметил комментарии к вопросу и мой собственный ответ, поэтому просто уточнить: мое предложение работает, когда n неизвестно. x должно быть известно, конечно. Все, что вам нужно сделать, это создать простой буфер, а затем заполнить буфер x строками и начать обработку.

Что касается реализации буфера, пока мы говорим о встроенных коллекциях Java, простой LinkedList - это все, что вам нужно. Так как вы будете вытягивать одну строку из буфера для каждой строки, которую вы в нее помещаете, ArrayList не будет хорошо работать с постоянным сдвигом индексов массива. Вообще говоря, буфер с массивом должен быть круглым, чтобы избежать плохой производительности.

6 голосов
/ 13 декабря 2011

В этом посте я предоставлю вам два совершенно разных подхода к решению вашей проблемы, и в зависимости от вашего варианта использования одно из решений подойдет лучше, чем другое.

Альтернатива # 1

Этот метод является эффективным с точки зрения памяти, но довольно сложным, если вы собираетесь пропустить много содержимого, этот метод рекомендуется, так как вы будете хранить только одну строку за раз в памяти во время обработки.

Реализация этого в этом посте не может быть супер оптимизированной, но теория, стоящая за этим, ясна.

Вы начнете с чтения файла в обратном направлении, поиска N числа разрывов строк. Когда вы успешно найдете, где в файле вы хотите остановить обработку позже, вы вернетесь к началу файла.

Альтернатива # 2

Этот метод прост для понимания и очень прост. Во время выполнения в памяти будет храниться N строк, где N - количество строк, которые вы хотите пропустить в конце.

Строки будут храниться в контейнере FIFO ( First In, First Out ). Вы добавите последнюю прочитанную строку в FIFO, а затем удалите и обработаете первую запись. Таким образом, вы всегда будете обрабатывать строки как минимум на N записей от конца вашего файла.



Альтернатива # 1

Это может звучать странно, но это определенно выполнимо и способ, которым я бы порекомендовал вам сделать это; начать с чтения файла назад .

  1. Искать в конец файла
  2. Считывать (и удалять) байты (в начале файла), пока не найдете SKIP_N разрывы строк
  3. Сохранить эту позицию
  4. Искать в начале файла
  5. Читайте (и обрабатывайте) строки, пока не вернетесь к сохраненной позиции

Пример кода:

Приведенный ниже код удалит последние 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. Если ваш LinkedList<String> содержит больше строк, чем вы хотели бы пропустить, удалите первую запись и обработайте ее
  4. Повторяйте, пока не останется строк для чтения

Пример кода

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 голосов
/ 13 декабря 2011

Просто прочитайте x строк впереди. То есть есть очередь x строк.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...