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

Быстро прочитать последнюю строку текстового файла?

Какой самый быстрый и эффективный способ чтения последней строки текста из [очень, очень большого] файла в Java?

4b9b3361

Ответ 1

Посмотрите на мой ответ на похожий вопрос для С#. Код был бы очень похож, хотя поддержка кодирования несколько отличается в Java.

В принципе, это не очень простая вещь в целом. Как указывает MSalter, UTF-8 позволяет легко определить \r или \n, поскольку представление этих символов UTF-8 является таким же, как ASCII, и эти байты не будут встречаться в многобайтовом символе.

Итак, возьмите буфер (скажем) 2K и прогрессивно прочитайте назад (пропустите до 2K до того, как вы были до этого, прочитайте следующий 2K), проверяя завершение строки. Затем перейдите в нужное место в потоке, создайте InputStreamReader сверху и BufferedReader поверх этого. Затем просто вызовите BufferedReader.readLine().

Ответ 2

Ниже приведены две функции, одна из которых возвращает последнюю непустую строку файла без загрузки или перебора всего файла, а другая, которая возвращает последние N строк файла, не переходя через весь файл:

Какой хвост - это зум прямо к последнему символу файла, затем шаг назад, символ по символу, запись того, что он видит, пока не найдет разрыв строки. Как только он находит разрыв строки, он выходит из цикла. Обращает внимание на то, что было записано, и вставляет его в строку и возвращается. 0xA - новая строка, а 0xD - возврат каретки.

Если ваши строки заканчиваются \r\n или crlf или какой-либо другой "двойной строки новой строки новой строки", вам нужно будет указать n * 2 строки, чтобы получить последние n строк, потому что они подсчитывают 2 строки для каждой строки.

public String tail( File file ) {
    RandomAccessFile fileHandler = null;
    try {
        fileHandler = new RandomAccessFile( file, "r" );
        long fileLength = fileHandler.length() - 1;
        StringBuilder sb = new StringBuilder();

        for(long filePointer = fileLength; filePointer != -1; filePointer--){
            fileHandler.seek( filePointer );
            int readByte = fileHandler.readByte();

            if( readByte == 0xA ) {
                if( filePointer == fileLength ) {
                    continue;
                }
                break;

            } else if( readByte == 0xD ) {
                if( filePointer == fileLength - 1 ) {
                    continue;
                }
                break;
            }

            sb.append( ( char ) readByte );
        }

        String lastLine = sb.reverse().toString();
        return lastLine;
    } catch( java.io.FileNotFoundException e ) {
        e.printStackTrace();
        return null;
    } catch( java.io.IOException e ) {
        e.printStackTrace();
        return null;
    } finally {
        if (fileHandler != null )
            try {
                fileHandler.close();
            } catch (IOException e) {
                /* ignore */
            }
    }
}

Но вам, вероятно, не нужна последняя строка, вам нужны последние N строк, поэтому используйте это вместо:

public String tail2( File file, int lines) {
    java.io.RandomAccessFile fileHandler = null;
    try {
        fileHandler = 
            new java.io.RandomAccessFile( file, "r" );
        long fileLength = fileHandler.length() - 1;
        StringBuilder sb = new StringBuilder();
        int line = 0;

        for(long filePointer = fileLength; filePointer != -1; filePointer--){
            fileHandler.seek( filePointer );
            int readByte = fileHandler.readByte();

             if( readByte == 0xA ) {
                if (filePointer < fileLength) {
                    line = line + 1;
                }
            } else if( readByte == 0xD ) {
                if (filePointer < fileLength-1) {
                    line = line + 1;
                }
            }
            if (line >= lines) {
                break;
            }
            sb.append( ( char ) readByte );
        }

        String lastLine = sb.reverse().toString();
        return lastLine;
    } catch( java.io.FileNotFoundException e ) {
        e.printStackTrace();
        return null;
    } catch( java.io.IOException e ) {
        e.printStackTrace();
        return null;
    }
    finally {
        if (fileHandler != null )
            try {
                fileHandler.close();
            } catch (IOException e) {
            }
    }
}

Вызовите приведенные выше методы следующим образом:

File file = new File("D:\\stuff\\huge.log");
System.out.println(tail(file));
System.out.println(tail2(file, 10));

Внимание В диком западе юникода этот код может привести к ошибке выхода этой функции. Например, "Мэри" вместо "Мэри". Символы с шляпы, акценты, иероглифы и т.д. Могут привести к неправильному результату, потому что после символа добавляются акценты в качестве модификаторов. Обратные составные символы изменяют характер идентичности персонажа при развороте. Вам нужно будет выполнить полную проверку тестов на всех языках, на которых вы планируете использовать это.

Для получения дополнительной информации об этой проблеме разворота Юникода прочтите следующее: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

Ответ 4

Использование FileReader или FileInputStream не будет работать - вам нужно будет использовать FileChannel или RandomAccessFile, чтобы прокрутить файл назад с конца. Кодировки будут проблемой, хотя, как сказал Джон.

Ответ 5

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

MemoryMappedFile для печати последних 5 строк:

private static void printByMemoryMappedFile(File file) throws FileNotFoundException, IOException{
        FileInputStream fileInputStream=new FileInputStream(file);
        FileChannel channel=fileInputStream.getChannel();
        ByteBuffer buffer=channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
        buffer.position((int)channel.size());
        int count=0;
        StringBuilder builder=new StringBuilder();
        for(long i=channel.size()-1;i>=0;i--){
            char c=(char)buffer.get((int)i);
            builder.append(c);
            if(c=='\n'){
                if(count==5)break;
                count++;
                builder.reverse();
                System.out.println(builder.toString());
                builder=null;
                builder=new StringBuilder();
            }
        }
        channel.close();
    }

RandomAccessFile для печати последних 5 строк:

private static void printByRandomAcessFile(File file) throws FileNotFoundException, IOException{
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
        int lines = 0;
        StringBuilder builder = new StringBuilder();
        long length = file.length();
        length--;
        randomAccessFile.seek(length);
        for(long seek = length; seek >= 0; --seek){
            randomAccessFile.seek(seek);
            char c = (char)randomAccessFile.read();
            builder.append(c);
            if(c == '\n'){
                builder = builder.reverse();
                System.out.println(builder.toString());
                lines++;
                builder = null;
                builder = new StringBuilder();
                if (lines == 5){
                    break;
                }
            }

        }
    }

Ответ 7

try(BufferedReader reader = new BufferedReader(new FileReader(reqFile))) {

    String line = null;

    System.out.println("======================================");

    line = reader.readLine();       //Read Line ONE
    line = reader.readLine();       //Read Line TWO
    System.out.println("first line : " + line);

    //Length of one line if lines are of even length
    int len = line.length();       

    //skip to the end - 3 lines
    reader.skip((reqFile.length() - (len*3)));

    //Searched to the last line for the date I was looking for.

    while((line = reader.readLine()) != null){

        System.out.println("FROM LINE : " + line);
        String date = line.substring(0,line.indexOf(","));

        System.out.println("DATE : " + date);      //BAM!!!!!!!!!!!!!!
    }

    System.out.println(reqFile.getName() + " Read(" + reqFile.length()/(1000) + "KB)");
    System.out.println("======================================");
} catch (IOException x) {
    x.printStackTrace();
}

Ответ 8

Насколько я знаю, самый быстрый способ чтения последней строки текстового файла - это использование класса Apache FileUtils, который находится в "org.apache.commons.io". У меня есть файл с двумя миллионами строк, и с помощью этого класса мне понадобилось менее одной секунды, чтобы найти последнюю строку. Вот мой код:

LineIterator lineIterator = FileUtils.lineIterator(newFile(filePath),"UTF-8");
String lastLine="";
while (lineIterator.hasNext()){
 lastLine=  lineIterator.nextLine();
}

Ответ 9

Path path = Paths.get(pathString);
      List<String> allLines = Files.readAllLines(path);
      return allLines.get(allLines.size()-1);