파이썬으로 큰 텍스트 파일을 메모리에로드하지 않고 한 줄씩 읽을 수 있습니까?


239

큰 파일을 한 줄씩 읽어야합니다. 파일에 5GB 이상이 있고 각 줄을 읽어야한다고 말하지만 분명히 readlines()메모리에 매우 큰 목록을 생성 하기 때문에 사용하고 싶지 않습니다 .

이 경우 아래 코드가 어떻게 작동합니까? 되어 xreadlines자체 메모리에 하나 하나 읽어? 생성기 표현식이 필요합니까?

f = (line for line in open("log.txt").xreadlines())  # how much is loaded in memory?

f.next()  

또한 Linux tail명령 과 마찬가지로 이것을 역순으로 읽으려면 어떻게해야 합니까?

나는 찾았다 :

http://code.google.com/p/pytailer/

" 파이썬 헤드, 테일 및 텍스트 파일의 행으로 뒤로 읽음 "

둘 다 잘 작동했습니다!


그리고 꼬리에서 이것을 읽으려면 어떻게해야합니까? 마지막 줄부터 시작하여 한 줄씩.
Bruno Rocha-rochacbruno

이것은 별도의 질문을해야한다
cmcginty

답변:


310

Keith는 간결하면서 파일을 명시 적으로 닫지 않기 때문에이 답변을 제공했습니다.

with open("log.txt") as infile:
    for line in infile:
        do_something_with(line)

30
문제는 여전히 "file in line"이 5GB 줄을 메모리에로드한다는 것입니다. 그리고 꼬리에서 어떻게 읽을 수 있습니까?
Bruno Rocha-rochacbruno

66
@rochacbruno, 한 번에 한 줄만 읽습니다. 다음 줄을 읽을 때, 다른 곳에서 참조를 저장하지 않는 한 이전 줄은 가비지 수집됩니다
John La Rooy

1
@rochacbruno, 역순으로 줄을 읽는 것은 불행히도 효율적으로하기가 쉽지 않습니다. 일반적으로 당신이 재치있는 크기의 청크 파일의 끝에서 읽고 싶은 것 개행 문자에 분할 (메가 바이트 말에 킬로바이트) (또는 문자를 종료 어떤 라인은 플랫폼에)
존 라 Rooy


1
@bawejakunal, 한 줄이 너무 길어서 메모리에 한 번에로드 할 수 없다면? 텍스트 파일 에서는 드문 경우입니다 . for행을 반복 하는 루프를 사용하는 대신 chunk = infile.read(chunksize)내용에 관계없이 제한된 크기의 청크를 읽는 데 사용할 수 있습니다 . 청크 내부에서 개행을 직접 검색해야합니다.
John La Rooy

60

파일 객체를 반복자로 사용하기 만하면됩니다.

for line in open("log.txt"):
    do_something_with(line)

최신 Python 버전에서 컨텍스트 관리자를 사용하는 것이 더 좋습니다.

with open("log.txt") as fileobject:
    for line in fileobject:
        do_something_with(line)

파일도 자동으로 닫힙니다.


2
그것은 전체 파일을 메모리에로드하지 않습니까?
Bruno Rocha-rochacbruno

17

구식 접근 방식 :

fh = open(file_name, 'rt')
line = fh.readline()
while line:
    # do stuff with line
    line = fh.readline()
fh.close()

2
사소한 설명 : 예외 안전을 위해 'with'문을 사용하는 것이 좋습니다. "open (filename, 'rt')를 fh :"로 사용
prokher

16
@ prokher : 예,하지만 저는 이것을 "구식 학교"라고했습니다.
PTBNL

15

대신 이터레이터를 사용하는 것이 좋습니다. 관련 : http://docs.python.org/library/fileinput.html

문서에서 :

import fileinput
for line in fileinput.input("filename"):
    process(line)

이렇게하면 전체 파일을 한 번에 메모리에 복사하지 않아도됩니다.


문서는 스 니펫을 "일반 사용"으로 표시하지만 루프를 완료 할 때 close()반환 된 FileInput클래스 객체 의 메서드를 호출하지 않으므로이 방법을 사용하지 마십시오. Python 3.2에서는 마침내이 fileinput문제를 해결하는 컨텍스트 관리자 프로토콜과 호환됩니다 (그러나 코드는 여전히 표시되지 않았습니다).
martineau

7

파일에 줄 바꿈이없는 경우 수행 할 작업은 다음과 같습니다.

with open('large_text.txt') as f:
  while True:
    c = f.read(1024)
    if not c:
      break
    print(c)

이 방법이 마음에 들지만 텍스트의 줄이 덩어리로 나눌 위험이 있습니다. 나는 이것을 개인적으로 보았습니다. 즉, 파일처럼 sstring을 검색하는 경우 줄이 덩어리로 인해 일부를 놓칠 것입니다. 이 문제를 해결할 방법이 있습니까? @Ariel Cabib
edo101

6

이것을 시도하십시오 :

with open('filename','r',buffering=100000) as f:
    for line in f:
        print line

설명 해주십시오?
Nikhil VJ

3
파이썬 공식 문서에서 : link 옵션 버퍼링 인자는 파일의 원하는 버퍼 크기를 지정합니다 : 0은 버퍼링되지 않음을 의미하고 1은 버퍼링 된 라인을 의미합니다. 다른 양수 값은 해당 크기 (바이트)의 버퍼를 사용한다는 의미입니다. 네거티브 버퍼링은 시스템 기본값을 사용하는 것을 의미합니다. 일반적으로 tty 장치의 경우 라인 버퍼링되고 다른 파일의 경우 완전히 버퍼링됩니다. 생략하면 시스템 기본값이 사용됩니다
jyoti das

내 경우에는 두 개의 파일 처리기 (하나는 읽기, 다른 쓰기)가있는 ~ 4GB 이상의 파일로 하루를 절약했습니다. 파이썬이 중단되어 이제는 괜찮습니다! 감사.
Xelt

@jyotidas이 방법이 마음에 들지만 텍스트의 줄이 여러 덩어리로 나눌 위험이 있습니다. 나는 이것을 개인적으로 보았습니다. 즉, 파일처럼 sstring을 검색하는 경우 줄이 덩어리로 인해 일부를 놓칠 것입니다. 이 문제를 해결할 방법이 있습니까? 내가 잘못 계산했을 때 readlines를 사용하는 것이 효과가 없었습니다
edo101

3

@ john-la-rooy의 대답처럼 쉽게 보일 수 있다고 생각할 수 없었습니다. 그래서 cp한 줄씩 읽고 쓰는 명령을 다시 작성했습니다. CRAZY FAST입니다.

#!/usr/bin/env python3.6

import sys

with open(sys.argv[2], 'w') as outfile:
    with open(sys.argv[1]) as infile:
        for line in infile:
            outfile.write(line)

참고 : 파이썬은 readline줄 끝을 표준화 하기 때문에 DOS 줄 끝이있는 문서 \r\n를 Unix 줄 끝의 로 변환하는 부작용이 있습니다 \n. 이 주제를 검색 한 이유는 개발자가 다양한 .NET 라이브러리를 맹목적으로 사용했기 때문에 줄 끝이 뒤섞인 로그 파일을 변환해야했기 때문입니다. 초기 속도 테스트 후 rstrip줄을 다시 갈 필요가 없다는 사실에 놀랐습니다 . 이미 완벽했습니다!
Bruno Bronosky

2

불꽃 프로젝트는 최근 6 년 동안 먼 길을왔다. 팬더 기능의 유용한 하위 세트를 다루는 간단한 API가 있습니다.

dask.dataframe 은 내부적으로 청킹을 처리하고 병렬화 가능한 많은 작업을 지원하며 인 메모리 작업을 위해 슬라이스를 팬더로 쉽게 내보낼 수 있습니다.

import dask.dataframe as dd

df = dd.read_csv('filename.csv')
df.head(10)  # return first 10 rows
df.tail(10)  # return last 10 rows

# iterate rows
for idx, row in df.iterrows():
    ...

# group by my_field and return mean
df.groupby(df.my_field).value.mean().compute()

# slice by column
df[df.my_field=='XYZ'].compute()

2

다음은 메모리 문제를 일으키지 않고 모든 크기의 텍스트 파일을로드하는 코드입니다. 기가 바이트 크기의 파일을 지원합니다

https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d

data_loading_utils.py 파일을 다운로드 하여 코드로 가져 오기

용법

import data_loading_utils.py.py
file_name = 'file_name.ext'
CHUNK_SIZE = 1000000


def process_lines(data, eof, file_name):

    # check if end of file reached
    if not eof:
         # process data, data is one single line of the file

    else:
         # end of file reached

data_loading_utils.read_lines_from_file_as_data_chunks(file_name, chunk_size=CHUNK_SIZE, callback=self.process_lines)

process_lines 메소드는 콜백 함수입니다. 한 번에 파일의 한 줄을 나타내는 매개 변수 데이터와 함께 모든 행에 대해 호출됩니다.

머신 하드웨어 구성에 따라 변수 CHUNK_SIZE를 구성 할 수 있습니다 .


이 방법이 마음에 들지만 텍스트의 줄이 덩어리로 나눌 위험이 있습니다. 나는 이것을 개인적으로 보았습니다. 즉, 파일처럼 sstring을 검색하는 경우 줄이 덩어리로 인해 일부를 놓칠 것입니다. 이 문제를 해결할 방법이 있습니까? 내가 잘못 계산했을 때 readlines를 사용하는 것이 효과가 없었습니다
edo101

0

이건 어때요? 파일을 읽을 때 운영 체제가 다음 줄을 캐시하기 때문에 파일을 청크로 나누고 한 줄씩 읽습니다. 파일을 한 줄씩 읽으면 캐시 된 정보를 효율적으로 사용하지 않는 것입니다.

대신 파일을 청크로 나누고 전체 청크를 메모리에로드 한 다음 처리하십시오.

def chunks(file,size=1024):
    while 1:

        startat=fh.tell()
        print startat #file's object current position from the start
        fh.seek(size,1) #offset from current postion -->1
        data=fh.readline()
        yield startat,fh.tell()-startat #doesnt store whole list in memory
        if not data:
            break
if os.path.isfile(fname):
    try:
        fh=open(fname,'rb') 
    except IOError as e: #file --> permission denied
        print "I/O error({0}): {1}".format(e.errno, e.strerror)
    except Exception as e1: #handle other exceptions such as attribute errors
        print "Unexpected error: {0}".format(e1)
    for ele in chunks(fh):
        fh.seek(ele[0])#startat
        data=fh.read(ele[1])#endat
        print data

이것은 유망 해 보인다. 바이트 또는 라인별로로드됩니까? 바이트 단위 인 경우 줄이 끊어 질까 두렵습니다. 한 번에 1000 줄을로드하여 처리 할 수 ​​있습니까?
Nikhil VJ

0

감사합니다! 나는 최근에 파이썬 3으로 변환했으며 readlines (0)을 사용하여 큰 파일을 읽는 것에 좌절했습니다. 이것은 문제를 해결했다. 그러나 각 줄을 얻으려면 몇 가지 추가 단계를 수행해야했습니다. 각 줄 앞에는 "b '"가 있는데 이진 형식이라고 생각합니다. "decode (utf-8)"를 사용하여 ASCII를 변경했습니다.

그런 다음 각 줄의 중간에 "= \ n"을 제거해야했습니다.

그런 다음 줄을 줄 바꿈으로 나눕니다.

b_data=(fh.read(ele[1]))#endat This is one chunk of ascii data in binary format
        a_data=((binascii.b2a_qp(b_data)).decode('utf-8')) #Data chunk in 'split' ascii format
        data_chunk = (a_data.replace('=\n','').strip()) #Splitting characters removed
        data_list = data_chunk.split('\n')  #List containing lines in chunk
        #print(data_list,'\n')
        #time.sleep(1)
        for j in range(len(data_list)): #iterate through data_list to get each item 
            i += 1
            line_of_data = data_list[j]
            print(line_of_data)

다음은 Arohi 코드에서 "인쇄 데이터"바로 위에서 시작하는 코드입니다.


0

이 다른 질문에서 병렬 바이트 수준의 랜덤 액세스 방식을 시연했습니다.

readlines없이 텍스트 파일에서 줄 수 얻기

이미 제공된 답변 중 일부는 훌륭하고 간결합니다. 나는 그들 중 일부를 좋아한다. 그러나 실제로 파일에있는 데이터로 무엇을하고 싶은지에 달려 있습니다. 필자의 경우 큰 텍스트 파일에서 가능한 한 빨리 줄을 세고 싶었습니다. 내 코드는 다른 코드와 마찬가지로 다른 작업도 수행하도록 수정할 수 있습니다.


0

이것에 관해 가장 좋은 해결책을 찾았으며 330MB 파일로 시도했습니다.

lineno = 500
line_length = 8
with open('catfour.txt', 'r') as file:
    file.seek(lineno * (line_length + 2))
    print(file.readline(), end='')

여기서 line_length는 한 줄의 문자 수입니다. 예를 들어 "abcd"의 줄 길이는 4입니다.

'\ n'문자를 건너 뛰고 다음 문자로 이동하기 위해 줄 길이에 2를 추가했습니다.


-1

병렬로 작업하고 데이터 청크 만 읽지 만 새 줄로 깨끗하게 유지하려는 경우에 유용 할 수 있습니다.

def readInChunks(fileObj, chunkSize=1024):
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        while data[-1:] != '\n':
            data+=fileObj.read(1)
        yield data

-10
f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
    do_something_with(f1[i])

도움이 되었기를 바랍니다.


5
메모리의 전체 파일을 읽지 않습니까? 질문은 그것을 피하는 방법을 명시 적으로 묻기 때문에 질문에 대답하지 않습니다.
Fermi paradox
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.