Java: прочитайте последние n строк огромного файла

Я хочу прочитать последние n строк очень большого файла, не читая весь файл в любой буфер / область памяти с помощью Java.

Я осмотрел API JDK и Apache Commons I / O и не смог найти тот, который подходит для этой цели.

Я думал о том, как хвост или меньше делает это в UNIX. Я не думаю, что они загружают весь файл, а затем показывают последние несколько строк файла. Должен быть аналогичный способ сделать то же самое и в Java.

9 ответов


если вы используете RandomAccessFile, вы можете использовать length и seek чтобы попасть в конкретную точку в конце файла, а затем читать дальше.

если вы обнаружите, что было недостаточно линий, отступите с этой точки и повторите попытку. Как только вы выяснили, где N - Я последняя строка начинается, вы можете искать там и просто читать и печатать.

первоначальное предположение наилучшего предположения может быть сделано на основе ваших данных свойства. Например, если это текстовый файл, возможно, длина строки не будет превышать в среднем 132, поэтому, чтобы получить последние пять строк, начните 660 символов до конца. Затем, если вы ошиблись, попробуйте еще раз в 1320 (вы даже можете использовать то, что вы узнали из последних 660 символов, чтобы настроить это-пример: если эти 660 символов были всего три строки, следующая попытка может быть 660 / 3 * 5, плюс, может быть, немного больше на всякий случай).


Я нашел самый простой способ сделать с помощью ReversedLinesFileReader С Apache commons-io API-интерфейс. Этот метод даст вам строку снизу вверх файла, и вы можете указать n_lines значение для указания количества линий.

import org.apache.commons.io.input.ReversedLinesFileReader;


File file = new File("D:\file_name.xml");
int n_lines = 10;
int counter = 0; 
ReversedLinesFileReader object = new ReversedLinesFileReader(file);
while(counter < n_lines) {
    System.out.println(object.readLine());
    counter++;
}

RandomAccessFile-хорошее место для начала, как описано в других ответах. Есть один важный нюанс хотя.

если ваш файл не закодирован с кодировкой один байт на символ,readLine() метод не будет работать для вас. И readUTF() не будет работать ни при каких обстоятельствах. (Он читает строку, которой предшествует число символов ...)

вместо этого вам нужно будет убедиться, что вы ищете маркеры конца строки таким образом, чтобы уважать границы символов кодировки. Для кодировок фиксированной длины (например, вкусы UTF-16 или UTF-32) необходимо извлечь символы, начиная с байтовых позиций, которые делятся на размер символов в байтах. Для кодировок переменной длины (например, UTF-8) вам нужно найти байт, который должны быть первым байтом символа.

в случае UTF-8 первым байтом символа будет 0xxxxxxx или 110xxxxx или 1110xxxx или 11110xxx. Все остальное - либо второй / третий байт, либо незаконная последовательность UTF-8. См.Стандарт Unicode, Версия 5.2, Глава 3.9, таблица 3-7. Это означает, как указывает обсуждение комментариев, что любые байты 0x0A и 0x0D в правильно закодированном потоке UTF-8 будут представлять символ LF или CR. Таким образом, подсчет байтов является допустимой стратегией реализации (для UTF-8).

определив правильную границу символа, вы можете просто позвонить new String(...) передача массива байтов, смещение, количество и кодирование, а затем повторно вызовите String.lastIndexOf(...) для подсчета конца строк.


нашел RandomAccessFile и другие классы чтения буферов слишком медленные для меня. Ничто не может быть быстрее, чем tail -<#lines>. Так что это было лучшим решением для меня.

public String getLastNLogLines(File file, int nLines) {
    StringBuilder s = new StringBuilder();
    try {
        Process p = Runtime.getRuntime().exec("tail -"+nLines+" "+file);
        java.io.BufferedReader input = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
        String line = null;
    //Here we first read the next line into the variable
    //line and then check for the EOF condition, which
    //is the return value of null
    while((line = input.readLine()) != null){
            s.append(line+'\n');
        }
    } catch (java.io.IOException e) {
        e.printStackTrace();
    }
    return s.toString();
}

CircularFifoBuffer из Apache commons . ответ на аналогичный вопрос в как читать последние 5 строк .txt файл в java

обратите внимание, что в Apache Commons Collections 4 Этот класс, похоже, был переименован в CircularFifoQueue


A RandomAccessFile позволяет искать (http://download.oracle.com/javase/1.4.2/docs/api/java/io/RandomAccessFile.html). The File.length метод вернет размер файла. Проблема заключается в определении количества строк. Для этого вы можете искать до конца файла и читать назад, пока не попадете в нужное количество строк.


У меня была аналогичная проблема, но я не понял других решений.

я использовал это. Надеюсь, это простой код.

// String filePathName = (direction and file name).
File f = new File(filePathName);
long fileLength = f.length(); // Take size of file [bites].
long fileLength_toRead = 0;
if (fileLength > 2000) {
    // My file content is a table, I know one row has about e.g. 100 bites / characters. 
    // I used 1000 bites before file end to point where start read.
    // If you don't know line length, use @paxdiablo advice.
    fileLength_toRead = fileLength - 1000;
}
try (RandomAccessFile raf = new RandomAccessFile(filePathName, "r")) { // This row manage open and close file.
    raf.seek(fileLength_toRead); // File will begin read at this bite. 
    String rowInFile = raf.readLine(); // First readed line usualy is not whole, I needn't it.
    rowInFile = raf.readLine();
    while (rowInFile != null) {
        // Here I can readed lines (rowInFile) add to String[] array or ArriyList<String>.
        // Later I can work with rows from array - last row is sometimes empty, etc.
        rowInFile = raf.readLine();
    }
}
catch (IOException e) {
    //
}

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

public static void tail(File src, OutputStream out, int maxLines) throws FileNotFoundException, IOException {
    BufferedReader reader = new BufferedReader(new FileReader(src));
    String[] lines = new String[maxLines];
    int lastNdx = 0;
    for (String line=reader.readLine(); line != null; line=reader.readLine()) {
        if (lastNdx == lines.length) {
            lastNdx = 0;
        }
        lines[lastNdx++] = line;
    }

    OutputStreamWriter writer = new OutputStreamWriter(out);
    for (int ndx=lastNdx; ndx != lastNdx-1; ndx++) {
        if (ndx == lines.length) {
            ndx = 0;
        }
        writer.write(lines[ndx]);
        writer.write("\n");
    }

    writer.flush();
}

    int n_lines = 1000;
    ReversedLinesFileReader object = new ReversedLinesFileReader(new File(path));
    String result="";
    for(int i=0;i<n_lines;i++){
        String line=object.readLine();
        if(line==null)
            break;
        result+=line;
    }
    return result;