Program Club

텍스트 파일의 첫 줄과 마지막 줄을 얻는 가장 효율적인 방법은 무엇입니까?

proclub 2020. 11. 6. 20:57
반응형

텍스트 파일의 첫 줄과 마지막 줄을 얻는 가장 효율적인 방법은 무엇입니까?


각 줄에 타임 스탬프가 포함 된 텍스트 파일이 있습니다. 내 목표는 시간 범위를 찾는 것입니다. 모든 시간이 순서대로되어 있으므로 첫 번째 줄이 가장 빠른 시간이되고 마지막 줄이 가장 늦은 시간이됩니다. 나는 맨 처음과 맨 마지막 줄만 필요합니다. 파이썬에서 이러한 줄을 얻는 가장 효율적인 방법은 무엇입니까?

참고 : 이러한 파일은 길이가 각각 약 1-2 백만 줄로 비교적 길고 수백 개의 파일에 대해이 작업을 수행해야합니다.


io 모듈 용 문서

with open(fname, 'rb') as fh:
    first = next(fh).decode()

    fh.seek(-1024, 2)
    last = fh.readlines()[-1].decode()

여기서 변수 값은 1024이며 평균 문자열 길이를 나타냅니다. 예를 들어 1024 만 선택합니다. 평균 선 길이를 추정 한 경우 해당 값에 2를 곱한 값을 사용할 수 있습니다.

줄 길이에 대해 가능한 상한에 대해 전혀 모르기 때문에 확실한 해결책은 파일을 반복하는 것입니다.

for line in fh:
    pass
last = line

그냥 사용할 수있는 바이너리 플래그로 귀찮게 할 필요가 없습니다 open(fname).

ETA : 작업 할 파일이 많으므로 random.sample마지막 줄의 길이를 확인하기 위해이 코드를 사용하여 수십 개의 파일 샘플을 만들고 실행할 수 있습니다 . 우선적으로 큰 위치 이동 값을 사용합니다 (1MB). 이렇게하면 전체 실행에 대한 값을 추정하는 데 도움이됩니다.


읽기 위해 파일을 열고 builtin을 사용하여 첫 번째 줄을 읽은 readline()다음 파일의 끝으로 이동하여 해당 줄의 이전 EOL 을 찾고 거기에서 마지막 줄을 읽을 때까지 뒤로 이동할 수 있습니다.

with open(file, "rb") as f:
    first = f.readline()        # Read the first line.
    f.seek(-2, os.SEEK_END)     # Jump to the second last byte.
    while f.read(1) != b"\n":   # Until EOL is found...
        f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more.
    last = f.readline()         # Read last line.

마지막 바이트 대신 두 번째 마지막 바이트로 점프하면 후행 EOL로 인해 직접 반환되지 않습니다. 뒤로 이동하는 동안 EOL을 읽고 확인하면 위치가 한 단계 앞으로 밀기 때문에 2 바이트 단계를 수행하는 것이 좋습니다.

사용하는 경우 seek형식 인 fseek(offset, whence=0)경우 whence오프셋 무슨 의미가를 기준으로합니다. docs.python.org 에서 인용 :

  • SEEK_SET또는 0= 스트림의 시작 부분에서 검색 (기본값); 오프셋은 TextIOBase.tell () 에서 반환 한 숫자 이거나 0이어야합니다. 다른 오프셋 값은 정의되지 않은 동작을 생성합니다.
  • SEEK_CUR또는 1= 현재 위치를“찾다”; 오프셋은 0이어야하며, 이는 작동하지 않습니다 (다른 모든 값은 지원되지 않음).
  • SEEK_END또는 2= 스트림의 끝을 찾습니다. 오프셋은 0이어야합니다 (다른 모든 값은 지원되지 않음).

총 6k 라인이 200kB 인 파일에서 timeit을 10k 번 실행하면 이전에 제안 된 for 루프와 비교할 때 1.62s 대 6.92s가 나에게 제공되었습니다. 여전히 6k 라인이있는 1.3GB 크기의 파일을 사용하면 백 배가 8.93 대 86.95가되었습니다.

with open(file, "rb") as f:
    first = f.readline()     # Read the first line.
    for last in f: pass      # Loop through the whole file reading it all.

여기 당신이 원하는 것을 할 수있는 SilentGhost 답변의 수정 된 버전이 있습니다.

with open(fname, 'rb') as fh:
    first = next(fh)
    offs = -100
    while True:
        fh.seek(offs, 2)
        lines = fh.readlines()
        if len(lines)>1:
            last = lines[-1]
            break
        offs *= 2
    print first
    print last

여기에는 선 길이에 대한 상한이 필요하지 않습니다.


유닉스 명령을 사용할 수 있습니까? 나는 사용 head -1하고 tail -n 1아마도 가장 효율적인 방법 이라고 생각 합니다. 또는 간단한 fid.readline()사용 하여 첫 번째 줄과을 가져올 fid.readlines()[-1]수 있지만 너무 많은 메모리가 필요할 수 있습니다.


이것은 Python3 과도 호환되는 내 솔루션입니다. 국경 케이스도 관리하지만 utf-16 지원이 누락되었습니다.

def tail(filepath):
    """
    @author Marco Sulla (marcosullaroma@gmail.com)
    @date May 31, 2016
    """

    try:
        filepath.is_file
        fp = str(filepath)
    except AttributeError:
        fp = filepath

    with open(fp, "rb") as f:
        size = os.stat(fp).st_size
        start_pos = 0 if size - 1 < 0 else size - 1

        if start_pos != 0:
            f.seek(start_pos)
            char = f.read(1)

            if char == b"\n":
                start_pos -= 1
                f.seek(start_pos)

            if start_pos == 0:
                f.seek(start_pos)
            else:
                char = ""

                for pos in range(start_pos, -1, -1):
                    f.seek(pos)

                    char = f.read(1)

                    if char == b"\n":
                        break

        return f.readline()

그것은에 의해 ispired 것 Trasp의 대답AnotherParker의 코멘트 .


먼저 읽기 모드에서 파일을 연 다음 readlines () 메서드를 사용하여 한 줄씩 읽습니다. 목록에 저장된 모든 줄. 이제 목록 조각을 사용하여 파일의 첫 번째 줄과 마지막 줄을 가져올 수 있습니다.

    a=open('file.txt','rb')
    lines = a.readlines()
    if lines:
        first_line = lines[:1]
        last_line = lines[-1]

w=open(file.txt, 'r')
print ('first line is : ',w.readline())
for line in w:  
    x= line
print ('last line is : ',x)
w.close()

for루프는 라인을 통해 실행되며 x최종 반복의 마지막 라인을 가져옵니다.


반전 사용에 대해 언급 한 사람은 없습니다.

f=open(file,"r")
r=reversed(f.readlines())
last_line_of_file = r.next()

with open("myfile.txt") as f:
    lines = f.readlines()
    first_row = lines[0]
    print first_row
    last_row = lines[-1]
    print last_row

Here is an extension of @Trasp's answer that has additional logic for handling the corner case of a file that has only one line. It may be useful to handle this case if you repeatedly want to read the last line of a file that is continuously being updated. Without this, if you try to grab the last line of a file that has just been created and has only one line, IOError: [Errno 22] Invalid argument will be raised.

def tail(filepath):
    with open(filepath, "rb") as f:
        first = f.readline()      # Read the first line.
        f.seek(-2, 2)             # Jump to the second last byte.
        while f.read(1) != b"\n": # Until EOL is found...
            try:
                f.seek(-2, 1)     # ...jump back the read byte plus one more.
            except IOError:
                f.seek(-1, 1)
                if f.tell() == 0:
                    break
        last = f.readline()       # Read last line.
    return last

Getting the first line is trivially easy. For the last line, presuming you know an approximate upper bound on the line length, os.lseek some amount from SEEK_END find the second to last line ending and then readline() the last line.


with open(filename, "r") as f:
    first = f.readline()
    if f.read(1) == '':
        return first
    f.seek(-2, 2)  # Jump to the second last byte.
    while f.read(1) != b"\n":  # Until EOL is found...
        f.seek(-2, 1)  # ...jump back the read byte plus one more.
    last = f.readline()  # Read last line.
    return last

The above answer is a modified version of the above answers which handles the case that there is only one line in the file

참고URL : https://stackoverflow.com/questions/3346430/what-is-the-most-efficient-way-to-get-first-and-last-line-of-a-text-file

반응형