Development Tip

텍스트 파일의 마지막 줄을 빨리 읽으시겠습니까?

yourdevel 2020. 12. 10. 21:21
반응형

텍스트 파일의 마지막 줄을 빨리 읽으시겠습니까?


Java의 [매우 매우 큰] 파일에서 텍스트의 마지막 줄을 읽는 가장 빠르고 효율적인 방법은 무엇입니까?


C #에 대한 비슷한 질문에 대한 내 대답을 살펴보십시오 . Java에서는 인코딩 지원이 약간 다르지만 코드는 매우 유사합니다.

기본적으로 일반적으로하는 것은 매우 쉬운 일이 아닙니다. MSalter가 지적 하듯이, UTF-8은 쉽게 발견 할 수 있도록 않습니다 \r또는 \n그 문자의 UTF-8 표현은 ASCII처럼 동일로, 그 바이트 멀티 바이트 문자에서 발생하지 않습니다.

따라서 기본적으로 2K의 버퍼를 취하고 점진적으로 역방향으로 읽습니다 (이전에 2K로 건너 뛰고 다음 2K를 읽음). 그런 다음 스트림의 정확한 위치로 건너 뛰고 InputStreamReader맨 위에를 만들고 그 위에를 만듭니다 BufferedReader. 그런 다음 BufferedReader.readLine().


다음은 전체 파일을로드하거나 단계별로 실행하지 않고 파일의 비어 있지 않은 마지막 줄을 반환하는 함수와 전체 파일을 단계별로 실행하지 않고 파일의 마지막 N 줄을 반환하는 두 가지 함수입니다.

tail이하는 일은 파일의 마지막 문자로 곧바로 확대 한 다음 문자 단위로 뒤로 이동하여 줄 바꿈을 찾을 때까지 보이는 내용을 기록하는 것입니다. 줄 바꿈을 찾으면 루프에서 나갑니다. 기록 된 내용을 되돌리고 문자열에 던져 반환합니다. 0xA는 새 줄이고 0xD는 캐리지 리턴입니다.

줄 끝이 \r\n또는 crlf다른 "이중 줄 바꿈 스타일 줄 바꿈"인 경우 모든 줄에 대해 2 줄을 계산하므로 마지막 n 줄을 얻기 위해 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));

경고 유니 코드의 서부에서이 코드로 인해이 함수의 출력이 잘못 나올 수 있습니다. 예를 들어 "Mary 's"대신 "Mary? s"입니다. 와 캐릭터 모자, 악센트, 한자 등 악센트가 문자 다음 수식으로 추가되기 때문에 출력이 잘못 될 수 있습니다. 복합 문자를 반전하면 반전시 캐릭터의 정체성 특성이 변경됩니다. 이를 사용하려는 모든 언어에 대해 전체 배터리 테스트를 수행해야합니다.

이 유니 코드 반전 문제에 대한 자세한 내용은 http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx를 읽으십시오 .


Apache Commons에는 RandomAccessFile을 사용하는 구현이 있습니다.

ReversedLinesFileReader 라고 합니다.


FileReader 또는 FileInputStream을 사용하면 작동하지 않습니다. FileChannel 또는 RandomAccessFile사용 하여 파일을 끝에서 뒤로 반복해야합니다. Jon이 말했듯이 인코딩은 문제가 될 것입니다.


내가 아는 한 텍스트 파일의 마지막 줄을 읽는 가장 빠른 방법은 "org.apache.commons.io"에있는 FileUtils Apache 클래스를 사용하는 것입니다. 2 백만 줄의 파일이 있는데이 클래스를 사용하여 마지막 줄을 찾는 데 1 초도 걸리지 않았습니다. 내 코드는 다음과 같습니다.

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

아래 코드를 쉽게 변경하여 마지막 줄을 인쇄 할 수 있습니다.

마지막 5 줄을 인쇄하기위한 MemoryMappedFile :

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

마지막 5 줄을 인쇄하기위한 RandomAccessFile :

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

        }
    }

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

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

C # 에서는 스트림 위치를 설정할 수 있어야합니다.

From: 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
    }
}

참고URL : https://stackoverflow.com/questions/686231/quickly-read-the-last-line-of-a-text-file

반응형