복붙노트

[PYTHON] Python으로 파일의 마지막 x 줄을 검색하는 가장 효율적인 방법

PYTHON

Python으로 파일의 마지막 x 줄을 검색하는 가장 효율적인 방법

파일이 있는데 크기가 얼마나 클지는 모르지만 크기는 매우 다양합니다. 마지막 10 줄 정도를 검색하여 문자열과 일치하는지 확인하고 싶습니다. 가능한 한 신속하고 효율적으로이 작업을 수행해야하며 다음보다 나은 것이 있는지 궁금해하고 있습니다.

s = "foo"
last_bit = fileObj.readlines()[-10:]
for line in last_bit:
    if line == s:
        print "FOUND"

해결법

  1. ==============================

    1.

    # Tail
    from __future__ import with_statement
    
    find_str = "FIREFOX"                    # String to find
    fname = "g:/autoIt/ActiveWin.log_2"     # File to check
    
    with open(fname, "r") as f:
        f.seek (0, 2)           # Seek @ EOF
        fsize = f.tell()        # Get Size
        f.seek (max (fsize-1024, 0), 0) # Set pos @ last n chars
        lines = f.readlines()       # Read to end
    
    lines = lines[-10:]    # Get last 10 lines
    
    # This returns True if any line is exactly find_str + "\n"
    print find_str + "\n" in lines
    
    # If you're searching for a substring
    for line in lines:
        if find_str in line:
            print True
            break
    
  2. ==============================

    2.다음은 MizardX와 같은 대답입니다. 그러나 최악의 경우에 2 차 시간을 가져 와서 덩어리가 추가 될 때 줄 바꿈을 위해 작업 문자열을 반복적으로 재검토하는 문제는 없습니다.

    다음은 MizardX와 같은 대답입니다. 그러나 최악의 경우에 2 차 시간을 가져 와서 덩어리가 추가 될 때 줄 바꿈을 위해 작업 문자열을 반복적으로 재검토하는 문제는 없습니다.

    액티브 스테이트 솔루션 (이차원으로 보임)과 비교해 볼 때, 이것은 빈 파일이 주어지면 폭발하지 않으며, 블록 당 2 개가 아닌 하나를 찾습니다.

    산란하는 '꼬리'와 비교하면, 이것은 자급 자족합니다. (하지만 '꼬리'가 있다면 가장 좋습니다.)

    끝에서 수 kB를 잡아 먹는 것과 비교해 볼 때 충분하다고 생각하면 모든 라인 길이에서 작동합니다.

    import os
    
    def reversed_lines(file):
        "Generate the lines of file in reverse order."
        part = ''
        for block in reversed_blocks(file):
            for c in reversed(block):
                if c == '\n' and part:
                    yield part[::-1]
                    part = ''
                part += c
        if part: yield part[::-1]
    
    def reversed_blocks(file, blocksize=4096):
        "Generate blocks of file's contents in reverse order."
        file.seek(0, os.SEEK_END)
        here = file.tell()
        while 0 < here:
            delta = min(blocksize, here)
            here -= delta
            file.seek(here, os.SEEK_SET)
            yield file.read(delta)
    

    요청한대로 사용하려면 다음과 같이하십시오.

    from itertools import islice
    
    def check_last_10_lines(file, key):
        for line in islice(reversed_lines(file), 10):
            if line.rstrip('\n') == key:
                print 'FOUND'
                break
    

    편집 : map ()을 head ()의 itertools.imap ()으로 변경했습니다. 편집 2 : 단순화 된 reversed_blocks (). 3 편집 : 줄 바꿈 꼬리를 다시 스캔하지 마십시오. 편집 4 : BrianB가 알아 ​​차 렸듯이 str.splitlines ()가 마지막 '\ n'을 무시하기 때문에 reversed_lines ()를 다시 작성했습니다 (감사).

    아주 오래된 파이썬 버전에서 루프의 문자열 연결은 여기에 2 차 시간이 걸립니다. CPython은 적어도 지난 몇 년간이 문제를 자동으로 피할 수 있습니다.

  3. ==============================

    3.POSIX 시스템에서 파이썬을 사용하고 있다면 'tail -10'을 사용하여 마지막 몇 줄을 검색 할 수 있습니다. 마지막 10 줄을 얻기 위해 파이썬 코드를 작성하는 것보다 빠를 수도 있습니다. 파일을 직접 열지 말고 명령 'tail -10 filename'에서 파이프를 엽니 다. 로그 출력이 확실하다면 (예를 들어, 수백 또는 수천 자의 긴 줄이 절대로 없다는 것을 알고 있다면) '읽은 마지막 2KB'접근법 중 하나를 사용하면 괜찮을 것입니다.

    POSIX 시스템에서 파이썬을 사용하고 있다면 'tail -10'을 사용하여 마지막 몇 줄을 검색 할 수 있습니다. 마지막 10 줄을 얻기 위해 파이썬 코드를 작성하는 것보다 빠를 수도 있습니다. 파일을 직접 열지 말고 명령 'tail -10 filename'에서 파이프를 엽니 다. 로그 출력이 확실하다면 (예를 들어, 수백 또는 수천 자의 긴 줄이 절대로 없다는 것을 알고 있다면) '읽은 마지막 2KB'접근법 중 하나를 사용하면 괜찮을 것입니다.

  4. ==============================

    4.나는 파일의 마지막 2KB 정도를 읽는 것이 당신이 10 줄을 가져야하고, 자원 돼지를 너무 많이해서는 안된다고 생각한다.

    나는 파일의 마지막 2KB 정도를 읽는 것이 당신이 10 줄을 가져야하고, 자원 돼지를 너무 많이해서는 안된다고 생각한다.

    file_handle = open("somefile")
    file_size = file_handle.tell()
    file_handle.seek(max(file_size - 2*1024, 0))
    
    # this will get rid of trailing newlines, unlike readlines()
    last_10 = file_handle.read().splitlines()[-10:]
    
    assert len(last_10) == 10, "Only read %d lines" % len(last_10)
    
  5. ==============================

    5.다음은 상당히 효율적으로 보이는 mmap을 사용하는 버전입니다. 큰 장점은 mmap이 메모리 페이징 요구 사항에 맞게 파일을 자동으로 처리한다는 것입니다.

    다음은 상당히 효율적으로 보이는 mmap을 사용하는 버전입니다. 큰 장점은 mmap이 메모리 페이징 요구 사항에 맞게 파일을 자동으로 처리한다는 것입니다.

    import os
    from mmap import mmap
    
    def lastn(filename, n):
        # open the file and mmap it
        f = open(filename, 'r+')
        m = mmap(f.fileno(), os.path.getsize(f.name))
    
        nlcount = 0
        i = m.size() - 1 
        if m[i] == '\n': n += 1
        while nlcount < n and i > 0:
            if m[i] == '\n': nlcount += 1
            i -= 1
        if i > 0: i += 2
    
        return m[i:].splitlines()
    
    target = "target string"
    print [l for l in lastn('somefile', 10) if l == target]
    
  6. ==============================

    6.비슷한 것을해야 할 때 Manu Garg의 블로그 게시물에서 코드를 적용한 것을 기억합니다.

    비슷한 것을해야 할 때 Manu Garg의 블로그 게시물에서 코드를 적용한 것을 기억합니다.

  7. ==============================

    7.유닉스 박스에 있다면 os.popen ( "tail 10"+ filepath) .readlines ()가 가장 빠른 방법 일 것입니다. 그렇지 않으면 얼마나 강건한 지에 달려 있습니다. 지금까지 제안 된 방법들은 모두 어떤 식 으로든 무너질 것입니다. 가장 일반적인 경우의 견고 함과 속도를 위해 아마도 로그 검색과 같은 것을 원할 것입니다 : file.seek를 사용하여 파일 끝에서 1000 문자를 뺀 다음 읽어 와서 몇 라인을 포함하는지 확인한 다음 EOF에서 3000 문자를 뺀다. 2000자를 읽고 라인 수를 계산 한 다음 EOF -7000을 읽은 다음 4000 자로 읽고 라인 수를 계산합니다. 그러나 당신이 그것이 합당한 선 길이를 가진 파일들에서 항상 실행될 것이라는 것을 확실히 안다면, 그것을 필요로하지 않을 수도 있습니다.

    유닉스 박스에 있다면 os.popen ( "tail 10"+ filepath) .readlines ()가 가장 빠른 방법 일 것입니다. 그렇지 않으면 얼마나 강건한 지에 달려 있습니다. 지금까지 제안 된 방법들은 모두 어떤 식 으로든 무너질 것입니다. 가장 일반적인 경우의 견고 함과 속도를 위해 아마도 로그 검색과 같은 것을 원할 것입니다 : file.seek를 사용하여 파일 끝에서 1000 문자를 뺀 다음 읽어 와서 몇 라인을 포함하는지 확인한 다음 EOF에서 3000 문자를 뺀다. 2000자를 읽고 라인 수를 계산 한 다음 EOF -7000을 읽은 다음 4000 자로 읽고 라인 수를 계산합니다. 그러나 당신이 그것이 합당한 선 길이를 가진 파일들에서 항상 실행될 것이라는 것을 확실히 안다면, 그것을 필요로하지 않을 수도 있습니다.

    unix tail 명령의 소스 코드에서 영감을 얻을 수도 있습니다.

  8. ==============================

    8.나는 그 문제를 만났고, 대용량 syslog 파일의 마지막 시간을 파싱하고, activestate의 레서피 사이트에서이 함수를 사용했다 ... (http://code.activestate.com/recipes/439045/)

    나는 그 문제를 만났고, 대용량 syslog 파일의 마지막 시간을 파싱하고, activestate의 레서피 사이트에서이 함수를 사용했다 ... (http://code.activestate.com/recipes/439045/)

    !/usr/bin/env python
    # -*-mode: python; coding: iso-8859-1 -*-
    #
    # Copyright (c) Peter Astrand <astrand@cendio.se>
    
    import os
    import string
    
    class BackwardsReader:
        """Read a file line by line, backwards"""
        BLKSIZE = 4096
    
        def readline(self):
            while 1:
                newline_pos = string.rfind(self.buf, "\n")
                pos = self.file.tell()
                if newline_pos != -1:
                    # Found a newline
                    line = self.buf[newline_pos+1:]
                    self.buf = self.buf[:newline_pos]
                    if pos != 0 or newline_pos != 0 or self.trailing_newline:
                        line += "\n"
                    return line
                else:
                    if pos == 0:
                        # Start-of-file
                        return ""
                    else:
                        # Need to fill buffer
                        toread = min(self.BLKSIZE, pos)
                        self.file.seek(-toread, 1)
                        self.buf = self.file.read(toread) + self.buf
                        self.file.seek(-toread, 1)
                        if pos - toread == 0:
                            self.buf = "\n" + self.buf
    
        def __init__(self, file):
            self.file = file
            self.buf = ""
            self.file.seek(-1, 2)
            self.trailing_newline = 0
            lastchar = self.file.read(1)
            if lastchar == "\n":
                self.trailing_newline = 1
                self.file.seek(-1, 2)
    
    # Example usage
    br = BackwardsReader(open('bar'))
    
    while 1:
        line = br.readline()
        if not line:
            break
        print repr(line)
    

    그것은 매우 잘 작동하고 python은 전체 파일을 메모리로 읽어 들이고 마지막 10 개의 라인을 잘라내는 fileObj.readlines () [- 10 :]와 같은 것보다 훨씬 효율적입니다.

  9. ==============================

    9.당신은 당신이 10 개의 라인을 가질 때까지 파일의 끝에서부터 버퍼로 1,000 바이트 덩어리를 읽을 수 있습니다.

    당신은 당신이 10 개의 라인을 가질 때까지 파일의 끝에서부터 버퍼로 1,000 바이트 덩어리를 읽을 수 있습니다.

  10. ==============================

    10.바이트 오프셋을 추측하는 대신 파일을 뒤집을 때 줄 수를 계산할 수도 있습니다.

    바이트 오프셋을 추측하는 대신 파일을 뒤집을 때 줄 수를 계산할 수도 있습니다.

    lines = 0
    chunk_size = 1024
    
    f = file('filename')
    f.seek(0, 2)
    f.seek(f.tell() - chunk_size)
    
    while True:
        s = f.read(chunk_size)
        lines += s.count('\n')
        if lines > NUM_OF_LINES:
            break
        f.seek(f.tell() - chunk_size*2)
    

    이제 파일은 readlines ()를 실행할 수있는 좋은 위치에 있습니다. 또한 처음 읽은 문자열을 캐시하여 동일한 부분을 두 번 읽지 않아도됩니다.

  11. ==============================

    11.mmap을 사용하기 위해 mhawke의 제안을 받아서 rfind를 사용하는 버전을 작성했습니다.

    mmap을 사용하기 위해 mhawke의 제안을 받아서 rfind를 사용하는 버전을 작성했습니다.

    from mmap import mmap
    import sys
    
    def reverse_file(f):
        mm = mmap(f.fileno(), 0)
        nl = mm.size() - 1
        prev_nl = mm.size()
        while nl > -1:
            nl = mm.rfind('\n', 0, nl)
            yield mm[nl + 1:prev_nl]
            prev_nl = nl + 1
    
    def main():
        # Example usage
        with open('test.txt', 'r+') as infile:
            for line in reverse_file(infile):
                sys.stdout.write(line)
    
  12. ==============================

    12.파일의 마지막 몇 Ks를 읽은 다음 줄로 나눠서 마지막 10 개만 반환합니다.

    파일의 마지막 몇 Ks를 읽은 다음 줄로 나눠서 마지막 10 개만 반환합니다.

    그 덩어리의 시작이 선 경계에 떨어지는 것은 거의 불가능합니다. 그러나 어쨌든 첫 번째 선은 무시할 것입니다.

  13. ==============================

    13.개인적으로 필자는 쉘로 빠져 나와 tail -n10을 호출하여 파일을로드하려고합니다. 그렇지만 저는 파이썬 프로그래머가 아닙니다.)

    개인적으로 필자는 쉘로 빠져 나와 tail -n10을 호출하여 파일을로드하려고합니다. 그렇지만 저는 파이썬 프로그래머가 아닙니다.)

  14. ==============================

    14.첫째, 목록을 반환하는 함수 :

    첫째, 목록을 반환하는 함수 :

    def lastNLines(file, N=10, chunksize=1024):
        lines = None
        file.seek(0,2) # go to eof
        size = file.tell()
        for pos in xrange(chunksize,size-1,chunksize):
            # read a chunk
            file.seek(pos,2)
            chunk = file.read(chunksize)
            if lines is None:
                # first time
                lines = chunk.splitlines()
            else:
                # other times, update the 'first' line with
                # the new data, and re-split
                lines[0:1] = (chunk + lines[0]).splitlines()
            if len(lines) > N:
                return lines[-N:]
        file.seek(0)
        chunk = file.read(size-pos)
        lines[0:1] = (chunk + lines[0]).splitlines()
        return lines[-N:]
    

    둘째, 역순으로 행을 반복하는 함수입니다.

    def iter_lines_reversed(file, chunksize=1024):
        file.seek(0,2)
        size = file.tell()
        last_line = ""
        for pos in xrange(chunksize,size-1,chunksize):
            # read a chunk
            file.seek(pos,2)
            chunk = file.read(chunksize) + last_line
            # split into lines
            lines = chunk.splitlines()
            last_line = lines[0]
            # iterate in reverse order
            for index,line in enumerate(reversed(lines)):
                if index > 0:
                    yield line
        # handle the remaining data at the beginning of the file
        file.seek(0)
        chunk = file.read(size-pos) + last_line
        lines = chunk.splitlines()
        for line in reversed(lines):
            yield line
    

    귀하의 예를 들면 :

    s = "foo"
    for index, line in enumerate(iter_lines_reversed(fileObj)):
        if line == s:
            print "FOUND"
            break
        elif index+1 >= 10:
            break
    

    편집 : 이제 파일 크기를 자동으로 가져옵니다. 편집 2 : 이제는 10 줄만 반복합니다.

  15. ==============================

    15.이 솔루션은 파일을 한 번만 읽지 만 두 파일 객체 포인터를 사용하여 파일의 마지막 N 줄을 다시 읽지 않고 얻을 수 있습니다.

    이 솔루션은 파일을 한 번만 읽지 만 두 파일 객체 포인터를 사용하여 파일의 마지막 N 줄을 다시 읽지 않고 얻을 수 있습니다.

    def getLastLines (path, n):
        # return the las N lines from the file indicated in path
    
        fp = open(path)
        for i in range(n):
            line = fp.readline()
            if line == '':
                return []
    
        back = open(path)
        for each in fp:
            back.readline()
    
        result = []
        for line in back:
            result.append(line[:-1])
    
        return result
    
    
    
    
    s = "foo"
    last_bit = getLastLines(r'C:\Documents and Settings\ricardo.m.reyes\My Documents\desarrollo\tail.py', 10)
    for line in last_bit:
        if line == s:
            print "FOUND"
    
  16. ==============================

    16.아마 이것은 유용 할 수 있습니다 :

    아마 이것은 유용 할 수 있습니다 :

    import os.path
    
    path = 'path_to_file'
    os.system('tail -n1 ' + path)
    
  17. ==============================

    17.18 Darius Bacon의 솔루션 덕분에 30 % 빠른 구현 및 io.BaseIO 클래스로의 래핑이 가능합니다.

    18 Darius Bacon의 솔루션 덕분에 30 % 빠른 구현 및 io.BaseIO 클래스로의 래핑이 가능합니다.

    class ReverseFile(io.IOBase):
        def __init__ (self, filename, headers=1):
            self.fp = open(filename)
            self.headers = headers
            self.reverse = self.reversed_lines()
            self.end_position = -1
            self.current_position = -1
    
        def readline(self, size=-1):
            if self.headers > 0:
                self.headers -= 1
                raw = self.fp.readline(size)
                self.end_position = self.fp.tell()
                return raw
    
            raw = next(self.reverse)
            if self.current_position > self.end_position:
                return raw
    
            raise StopIteration
    
        def reversed_lines(self):
            """Generate the lines of file in reverse order.
            """
            part = ''
            for block in self.reversed_blocks():
                block = block + part
                block = block.split('\n')
                block.reverse()
                part = block.pop()
                if block[0] == '':
                    block.pop(0)
    
                for line in block:
                    yield line + '\n'
    
            if part:
                yield part
    
        def reversed_blocks(self, blocksize=0xFFFF):
            "Generate blocks of file's contents in reverse order."
            file = self.fp
            file.seek(0, os.SEEK_END)
            here = file.tell()
            while 0 < here:
                delta = min(blocksize, here)
                here -= delta
                file.seek(here, os.SEEK_SET)
                self.current_position = file.tell()
                yield file.read(delta)
    

    예제

    rev = ReverseFile(filename)
    for i, line in enumerate(rev):
            print("{0}: {1}".format(i, line.strip()))
    
  18. from https://stackoverflow.com/questions/260273/most-efficient-way-to-search-the-last-x-lines-of-a-file-in-python by cc-by-sa and MIT license