Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

Я нашел это самым простым способом сделать это с помощью 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++;
}

Ответ 3

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. Таким образом, простой подсчет байтов 0x0A и 0x0D является допустимой стратегией реализации (для UTF-8), если мы можем предположить, что другие виды разделителя строк Unicode (0x2028, 0x2029 и 0x0085) не используются. Вы не можете предположить это, тогда код был бы более сложным.

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

Ответ 4

Я обнаружил, что 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();
}

Ответ 6

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

Ответ 7

ReversedLinesFileReader можно найти в ja-библиотеке Apache Commons IO.

    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;

Ответ 8

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

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

// 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) {
    //
}

Ответ 9

package com.uday;

import java.io.File;
import java.io.RandomAccessFile;

public class TailN {
    public static void main(String[] args) throws Exception {
        long startTime = System.currentTimeMillis();

        TailN tailN = new TailN();
        File file = new File("/Users/udakkuma/Documents/workspace/uday_cancel_feature/TestOOPS/src/file.txt");
        tailN.readFromLast(file);

        System.out.println("Execution Time : " + (System.currentTimeMillis() - startTime));

    }

    public void readFromLast(File file) throws Exception {
        int lines = 3;
        int readLines = 0;
        StringBuilder builder = new StringBuilder();
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
            long fileLength = file.length() - 1;
            // Set the pointer at the last of the file
            randomAccessFile.seek(fileLength);

            for (long pointer = fileLength; pointer >= 0; pointer--) {
                randomAccessFile.seek(pointer);
                char c;
                // read from the last, one char at the time
                c = (char) randomAccessFile.read();
                // break when end of the line
                if (c == '\n') {
                    readLines++;
                    if (readLines == lines)
                        break;
                }
                builder.append(c);
                fileLength = fileLength - pointer;
            }
            // Since line is read from the last so it is in reverse order. Use reverse
            // method to make it correct order
            builder.reverse();
            System.out.println(builder.toString());
        }

    }
}

Ответ 10

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

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();
}

Ответ 11

Вот работа для этого.

    private static void printLastNLines(String filePath, int n) {
    File file = new File(filePath);
    StringBuilder builder = new StringBuilder();
    try {
        RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
        long pos = file.length() - 1;
        randomAccessFile.seek(pos);

        for (long i = pos - 1; i >= 0; i--) {
            randomAccessFile.seek(i);
            char c = (char) randomAccessFile.read();
            if (c == '\n') {
                n--;
                if (n == 0) {
                    break;
                }
            }
            builder.append(c);
        }
        builder.reverse();
        System.out.println(builder.toString());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}