Java 快速阅读文本文件的最后一行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/686231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:55:21  来源:igfitidea点击:

Quickly read the last line of a text file?

javafileio

提问by Jake

What's the quickest and most efficient way of reading the last line of text from a [very, very large] file in Java?

从 Java 中的 [非常非常大] 文件中读取最后一行文本的最快和最有效的方法是什么?

采纳答案by Jon Skeet

Have a look at my answer to a similar question for C#. The code would be quite similar, although the encoding support is somewhat different in Java.

看看我对 C#类似问题的回答。代码将非常相似,尽管 Java 中的编码支持有些不同。

Basically it's not a terribly easy thing to do in general. As MSalter points out, UTF-8 does make it easy to spot \ror \nas the UTF-8 representation of those characters is just the same as ASCII, and those bytes won't occur in multi-byte character.

基本上,这不是一件非常容易的事情。正如 MSalter 指出的那样,UTF-8 确实很容易被发现,\r或者\n因为这些字符的 UTF-8 表示与 ASCII 相同,并且这些字节不会出现在多字节字符中。

So basically, take a buffer of (say) 2K, and progressively read backwards (skip to 2K before you were before, read the next 2K) checking for a line termination. Then skip to exactly the right place in the stream, create an InputStreamReaderon the top, and a BufferedReaderon top of that. Then just call BufferedReader.readLine().

所以基本上,取一个(比如)2K 的缓冲区,并逐渐向后读取(在你之前跳到 2K,读取下一个 2K)检查行终止。然后跳到流中的正确位置,InputStreamReader在顶部创建一个,并在其顶部创建一个BufferedReader。然后只需调用BufferedReader.readLine().

回答by rball

In C#, you should be able to set the stream's position:

C# 中,您应该能够设置流的位置:

From: http://bytes.com/groups/net-c/269090-streamreader-read-last-line-text-file

来自:http: //bytes.com/groups/net-c/269090-streamreader-read-last-line-text-file

using(FileStream fs = File.OpenRead("c:\file.dat"))
{
    using(StreamReader sr = new StreamReader(fs))
    {
        sr.BaseStream.Position = fs.Length - 4;
        if(sr.ReadToEnd() == "DONE")
            // match
    }
}

回答by Michael Borgwardt

Using FileReader or FileInputStream won't work - you'll have to use either FileChannelor RandomAccessFileto loop through the file backwards from the end. Encodings will be a problem though, as Jon said.

使用 FileReader 或 FileInputStream 将不起作用 - 您必须使用FileChannelRandomAccessFile从末尾向后循环遍历文件。正如乔恩所说,编码将是一个问题。

回答by Eric Leschinski

Below are two functions, one that returns the last non-blank line of a file without loading or stepping through the entire file, and the other that returns the last N lines of the file without stepping through the entire file:

下面是两个函数,一个返回文件的最后一个非空行而不加载或单步执行整个文件,另一个返回文件的最后 N 行而不单步执行整个文件:

What tail does is zoom straight to the last character of the file, then steps backward, character by character, recording what it sees until it finds a line break. Once it finds a line break, it breaks out of the loop. Reverses what was recorded and throws it into a string and returns. 0xA is the new line and 0xD is the carriage return.

tail 的作用是直接缩放到文件的最后一个字符,然后一个字符一个字符地向后退,记录它看到的内容,直到找到换行符。一旦找到换行符,它就会跳出循环。反转记录的内容并将其放入字符串并返回。0xA 是新行,0xD 是回车。

If your line endings are \r\nor crlfor some other "double newline style newline", then you will have to specify n*2 lines to get the last n lines because it counts 2 lines for every line.

如果您的行尾是\r\ncrlf或其他一些“双换行样式换行符”,那么您必须指定 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 */
            }
    }
}

But you probably don't want the last line, you want the last N lines, so use this instead:

但是你可能不想要最后一行,你想要最后 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) {
            }
    }
}

Invoke the above methods like this:

像这样调用上面的方法:

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

WarningIn the wild west of unicode this code can cause the output of this function to come out wrong. For example "Mary?s" instead of "Mary's". Characters with hats, accents, Chinese charactersetc may cause the output to be wrong because accents are added as modifiers after the character. Reversing compound characters changes the nature of the identity of the character on reversal. You will have to do full battery of tests on all languages you plan to use this with.

警告在 unicode 的狂野西部,此代码可能导致此函数的输出出错。例如“Mary?s”而不是“Mary's”。带有帽子、重音、汉字等的字符可能会导致输出错误,因为在字符后添加重音作为修饰符。反转复合字符会改变反转时字符的特性。您必须对计划使用它的所有语言进行全面测试。

For more information about this unicode reversal problem read this: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

有关此 unicode 反转问题的更多信息,请阅读:http: //msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

回答by Trying

You can easily change the below code to print the last line.

您可以轻松更改以下代码以打印最后一行。

MemoryMappedFile for printing last 5 lines:

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 to print last 5 lines:

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

        }
    }

回答by jaco0646

Apache Commons has an implementation using RandomAccessFile.

Apache Commons 有一个使用RandomAccessFile的实现。

It's called ReversedLinesFileReader.

它被称为ReversedLinesFileReader

回答by Ajai Singh

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

回答by arash nadali

as far as I know The fastest way to read the last line of a text file is using FileUtils Apache class which is in "org.apache.commons.io". I have a two-million-line file and by using this class, it took me less than one second to find the last line. Here is the my code:

据我所知,读取文本文件最后一行的最快方法是使用“org.apache.commons.io”中的 FileUtils Apache 类。我有一个 200 万行的文件,通过使用这个类,我花了不到一秒钟的时间找到最后一行。这是我的代码:

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

回答by user2117229

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