2017-05-01 1 views
1

이 질문에 대한 답변은 이미 herehere입니다.파이썬 3에서 대용량 파일의 첫 줄을 효과적으로 제거하는 방법은 무엇입니까?

첫 번째 줄을 파이썬 3에서 큰 파일에서 효율적으로 제거하려면 어떻게합니까?

로깅이 필요한 프로그램을 작성 중이며 로그 파일의 최대 크기는 무한대로 구성 할 수 있습니다. 따라서 나는 이것들이 메모리 집약적 인 것처럼 readlines() 또는 유사한 방법을 사용하고 싶지 않습니다. 속도는 큰 문제는 아니지만, 없이 파일을 다시 작성하고 임시 파일없이 파일을 다시 작성하면 큰 효과가 있습니다.

솔루션은 크로스 플랫폼이어야합니다.

예 로그 파일 :

[09:14:56 07/04/17] [INFO] foo 
[23:45:01 07/04/17] [WARN] bar 
[13:45:28 08/04/17] [INFO] foobar 
... many thousands more lines 

출력 : 다음 해결 방법 중

while os.path.getsize(LOGFILE) > MAXLOGSIZE: 
    # remove first line of file 

를 수행해도 문제가 해결되지 않는 메모리입니다

:이 코드는 루프에서 실행됩니다

[23:45:01 07/04/17] [WARN] bar 
[13:45:28 08/04/17] [INFO] foobar 
... many thousands more lines 

효율적인 :

솔루션 # 1 - 작동하지만 비효율적 인은

with open('file.txt', 'r') as fin: 
    data = fin.read().splitlines(True) 
with open('file.txt', 'w') as fout: 
    fout.writelines(data[1:]) 

# 2 솔루션 - 작동하지 않습니다는 빈 파일

import shutil 

source_file = open('file.txt', 'r') 
source_file.readline() 
target_file = open('file.txt', 'w') 

shutil.copyfileobj(source_file, target_file) 

솔루션 # 3 잎 - 작품, 효율적인하지만, 추가 파일을 사용

with open("file.txt",'r') as f: 
    with open("new_file.txt",'w') as f1: 
     f.next() # skip header line 
     for line in f: 
      f1.write(line) 

답변

1

그래서이 접근법은 매우 해킹입니다. 라인 크기가 작은 표준 편차로 거의 같은 크기 인 경우 잘 작동합니다. 아이디어는 파일의 일부분을 메모리 효율이 될만큼 작지만 양 끝을 작성하는 것이 일을 망치지 않을만큼 충분히 큰 버퍼로 읽는 것입니다. (줄은 거의 차이가 없으므로 크기가 거의 같기 때문에 교차 할 수 있습니다. 우리의 손가락과 그것이기도 할 것이기를기도하십시오). 우리는 기본적으로 파일의 위치를 ​​추적하고 앞뒤로 이동합니다.

from collections import deque 
def efficient_dropfirst(f, dropfirst=1, buffersize=3): 
    f.seek(0) 
    buffer = deque() 
    tail_pos = 0 
    # these next two loops assume the file has many thousands of 
    # lines so we can safely drop and buffer the first few... 
    for _ in range(dropfirst): 
     f.readline() 
    for _ in range(buffersize): 
     buffer.append(f.readline()) 
    line = f.readline() 
    while line: 
     buffer.append(line) 
     head_pos = f.tell() 
     f.seek(tail_pos) 
     tail_pos += f.write(buffer.popleft()) 
     f.seek(head_pos) 
     line = f.readline() 
    f.seek(tail_pos) 
    # finally, clear out the buffer: 
    while buffer: 
     f.write(buffer.popleft()) 
    f.truncate() 
이제

은, 이제 잘 작동 척 파일이 밖으로 시도하자 : 그것은 양쪽에서 유리한 append 성능을 가지고 있기 때문에 나는 버퍼로 collections.deque을 사용하고, 우리는 큐의 FIFO 자연을 이용할 수 있습니다 : 마지막으로

>>> s = """1. the quick 
... 2. brown fox 
... 3. jumped over 
... 4. the lazy 
... 5. black dog. 
... 6. Old McDonald's 
... 7. Had a farm 
... 8. Eeyi Eeeyi Oh 
... 9. And on this farm they had a 
... 10. duck 
... 11. eeeieeeiOH 
... """ 

그리고 :

>>> import io 
>>> with io.StringIO(s) as f: # we mock a file 
...  efficient_dropfirst(f) 
...  final = f.getvalue() 
... 
>>> print(final) 
2. brown fox 
3. jumped over 
4. the lazy 
5. black dog. 
6. Old McDonald's 
7. Had a farm 
8. Eeyi Eeeyi Oh 
9. And on this farm they had a 
10. duck 
11. eeeieeeiOH 

이 확인을 해결해야 dropfirst <경우"슬랙"의 좋은 비트. 첫 번째 줄만 놓고 싶기 때문에 dropfirst=1으로 유지하면 buffersize=100 또는 뭔가 안전 할 수 있습니다. "수천 줄"을 읽는 것보다 훨씬 더 효율적이며, 한 줄이 이전 줄보다 크지 않으면 안전해야합니다. 그러나 이것은 경계 근처에서 매우 거친 경고입니다.

+0

광범위한 테스트를 거친 후 100 % 시간이 걸리는 것 같습니다. 코드에서 옳은 말을하는 것처럼 보입니다. 신뢰할 수 없게 행동해야합니다. 그러나 예기치 않은 신뢰성은 나에게 좋았다! – retnikt

+1

@retnikt 선 길이를 적용하면 (끝까지 도달하지 못하면 채우기가 끝나고 새 선이 생깁니다) 신뢰할 수있는 동작을 얻을 수 있습니다. 그보다 더 큰 문제가있을 수 있습니다 –

+1

len> 200에 대한 매우 긴 행이 있고 이전에 하나의 문자 만 포함 된 100 개의 행이 있다고 가정합니다 (예 : new - 라인), 그러면 실패 할 것이고, 유쾌하지는 않을 것이다. –

1

시도해보십시오. 언급 한대로 3 단계 접근 방식을 사용하지만 새 파일을 만들지는 않습니다.

filePath = r"E:\try.txt" 
file_str = "" 
with open(filePath,'r') as f: 
     f.next() # skip header line 
     for line in f: 
      file_str = file_str + line 

with open(filePath, "w") as f: 
    f.write(file_str) 
+0

메모리 효율적인 솔루션은 아닙니다. 또한, 나는이 오류가 발생합니다 : 'AttributeError : '_io.TextIOWrapper'객체에는 'next'' 속성이 없습니다. 타사 솔루션이기 때문에 다른 모듈이 필요합니까? – retnikt

+0

@retnikt 아니요. 왜냐하면 파이썬 3에서는'f.next()'대신'next (f)'를 사용해야하기 때문입니다. –

+1

맞습니다. 그것은 Python2 코드입니다. –

관련 문제