2012-10-06 12 views
9

비슷한 형식의 약 200 개의 짧은 텍스트 파일 (50kb)이 있습니다. 특정 문자열이 들어있는 각 파일에서 줄을 찾은 다음 해당 줄과 다음 세 줄 (파일의 나머지 줄은 제외)을 다른 텍스트 파일에 쓰려고합니다. 나는 이것을하기 위해 파이썬을 가르치려고 노력하고 있으며 이것을 시험해보기 위해 아주 단순하고 조잡한 작은 스크립트를 작성했습니다. 나는 2.6.5 버전을 사용 중이며 Mac 터미널에서 스크립트를 실행하고있다.파이썬을 사용하여 한 파일에서 다른 파일로 특정 줄을 작성하십시오.

#!/usr/bin/env python 

f = open('Test.txt') 

Lines=f.readlines() 
searchquery = 'am\n' 
i=0 

while i < 500: 
    if Lines[i] == searchquery: 
     print Lines[i:i+3] 
     i = i+1 
    else: 
     i = i+1 
f.close() 

이것은 다소 효과가 있으며 출력을 화면에 출력한다. 하지만 대신 새로운 파일에 줄을 인쇄하고 싶습니다. 그래서 다음과 같이 시도했습니다.

f1 = open('Test.txt') 
f2 = open('Output.txt', 'a') 

Lines=f1.readlines() 
searchquery = 'am\n' 
i=0 

while i < 500: 
if Lines[i] == searchquery: 
    f2.write(Lines[i]) 
    f2.write(Lines[i+1]) 
    f2.write(Lines[i+2]) 
    i = i+1 
else: 
    i = i+1 
f1.close() 
f2.close() 

그러나 파일에는 아무 것도 기록되지 않습니다. 나는 또한 시도했다

from __future__ import print_function 
print(Lines[i], file='Output.txt') 

그리고 어느 쪽이라도 일하게 할 수 없다. 누군가 내가 잘못하고있는 것을 설명 할 수 있거나 대신 시도해야하는 것에 대해 제안 할 수 있다면 정말 감사 할 것입니다. 또한 검색을 향상시키기위한 제안 사항이 있으면 감사하겠습니다. 내가 찾고자하는 문자열이 줄의 유일한 텍스트 인 테스트 파일을 사용했지만 실제 파일에서 필요한 줄은 여전히 ​​줄의 시작 부분에 있지만 다른 텍스트가 뒤 따랐다. 나는 내가 설정 한 것들이 실제로 작동하지 않을 것이라고 생각한다.

감사합니다. 매우 근사한 질문 인 경우 죄송합니다.

+2

두 번째 예에서는 루프 본문이 들여 쓰이지 않은 것처럼 보입니다. 복사/붙여 넣기 오류 또는 실제로 갖고있는 것입니까? – Collin

+2

아마도'enumerate' 함수와'for x in iterable' 구조를 살펴 봐야 할 것입니다. –

+0

@Collin 당신은 문제가 들여 쓰기 맞습니다.아마 두 시간 동안 그 코드를보고 결코 눈치 채지 못했습니다! 감사! – Andreanna

답변

17

바와 같이 @ajon 지적, 내가 들여 쓰기를 제외하고 코드와 근본적으로 아무 잘못이 있다고 생각하지 않습니다

당신은 같은 i=i+1 후까지 Lines[i] 경우에서 들여 쓰기를해야합니다. 들여 쓰기가 고정되면 그것은 나를 위해 작동합니다. 그러나 개선을위한 몇 가지 기회가 있습니다.

1) 파이썬에서 표준을 반복하는 방법은 for loop을 사용하는 것입니다. for 루프를 사용할 때 루프 카운터 변수를 정의 할 필요가 없으며 루프 카운터 변수를 직접 추적하여 사물을 반복 할 필요가 없습니다. 대신 다음과 같이 작성하십시오.

문자열 목록의 모든 항목을 반복하여 인쇄하십시오.

2) 대부분의 경우 이것이 사용자의 for 루프 모양입니다. 그러나 루프 카운트를 실제로 추적하려는 경우가 있습니다. 귀하는 한 줄뿐 아니라 다음 세 줄이 필요하므로 인덱싱을 위해 카운터를 사용해야하므로 귀하의 사례는 그러한 상황입니다 (lst[i]). 그게 enumerate()이고, 그 다음에는 루프를 반복 할 수있는 인덱스 인 의 인덱스를 반환합니다.

for i, line in enumerate(lines): 
    print i 
    print line 
    print lines[i+7] 

수동으로 예에서와 같이 루프 카운터를 추적했다, 두 가지가 있다면 다음 ifelse 블록 밖으로 이동해야i = i+1 그건

3). 두 경우 모두 그렇게하고 있기 때문에 if/else 뒤에 넣으십시오. 귀하의 경우에는 else 블록은 더 이상 아무것도하지 않으며, 제거 할 수 있습니다

while i < 500: 
    if Lines[i] == searchquery: 
     f2.write(Lines[i]) 
     f2.write(Lines[i+1]) 
     f2.write(Lines[i+2]) 
    i = i+1 

4) 지금이 500 개 라인보다 짧은 파일이있는 IndexError의 원인이됩니다. 루프 카운트를 500으로 하드 코딩하는 대신 반복되는 시퀀스의 실제 길이를 사용해야합니다. len(lines) 그 길이 줄 것입니다. 그러나 while 루프를 사용하는 대신 for 루프와 range(len(lst))을 사용하여 0에서 len(lst) - 1 범위의 목록을 반복 할 수 있습니다.

for i in range(len(lst)): 
    print lst[i] 

5)open()는 당신을 위해 파일을 닫는 돌봐 context manager로 사용할 수 있습니다. 컨텍스트 관리자는 다소 고급 개념이지만 이미 제공되어 있다면 사용하기가 매우 쉽습니다. 이

with open('test.txt') as f: 
    f.write('foo') 

같은 것을 수행하여 파일은 해당 with 블록 내부 f 당신에게 개방하고 액세스 할 수 있습니다. 블록을 벗어나면 파일이 자동으로 닫히기 때문에 파일을 닫지 않아도됩니다.

두 개의 파일을 여는 경우입니다. 이 두 with 제표 및 둥지를 사용하여 수행 할 수 있습니다 그들

with open('one.txt') as f1: 
    with open('two.txt') as f2: 
     f1.write('foo') 
     f2.write('bar') 

이나, 파이썬 2.7/파이썬 3.x의 단일 with 성명에서 두 개의 컨텍스트 관리자를 중첩하여 :

with open('one.txt') as f1, open('two.txt', 'a') as f2: 
     f1.write('foo') 
     f2.write('bar') 

6) 파일이 작성된 운영 체제에 따라 줄 끝이 다릅니다. 유닉스 계열 플랫폼에서는 \n이고, OSX 이전의 Mac은 \r이고, Windows는 \r\n입니다. 따라서 Lines[i] == searchquery은 Mac 또는 Windows 줄 끝과 일치하지 않습니다. file.readline()은 세 가지 모두를 처리 할 수 ​​있지만 줄 끝 부분에있는 모든 줄 끝을 유지하기 때문에 비교가 실패합니다.

searchquery = 'am' 
# ... 
      if line.strip() == searchquery: 
       # ... 

(file.read()을 사용하여 파일을 읽고 : 이것은 시작과 끝 부분에있는 모든 공백의 문자열을 제거하고,없이 그 결말 라인을 검색 패턴 비교되는, str.strip()를 사용하여 해결된다 str.splitlines()을 사용하는 것이 다른 대안 일 수 있습니다.당신이 실제로 검색 문자열을 언급 한 줄의 시작 부분에 나타납니다 때문에)

그러나, str.startswith()를 사용하여이 작업을 수행 할 수 있습니다

if line.startswith(searchquery): 
    # ... 

7) 파이썬, PEP8 공식 스타일 가이드, 변수 (function), 속성 (attributes), 메소드 (methods), 모듈 (modules), 패키지 (packages) 등 거의 모든 것들을 클래스로 CamelCase, lowercase_underscore을 사용할 것을 권장합니다. 따라서 Lines 대신 lines을 사용하십시오. 이것은 다른 것들과 비교했을 때 분명히 사소한 것이지만 여전히 일찍부터 가치가있는 가치가 있습니다.


그래서, 모든 것을 고려 나는 다음과 같은 코드를 작성합니다

searchquery = 'am' 

with open('Test.txt') as f1: 
    with open('Output.txt', 'a') as f2: 
     lines = f1.readlines() 
     for i, line in enumerate(lines): 
      if line.startswith(searchquery): 
       f2.write(line) 
       f2.write(lines[i + 1]) 
       f2.write(lines[i + 2]) 

@TomK가 지적했듯이,이 모든 코드는 검색 문자열에 일치하는 경우, 적어도 두 줄이 있다고 가정합니다 그 다음. 그 가정에 의지 할 수 없다면 @poorsod와 같은 블록을 사용하여이 경우를 처리하는 것이 올바른 방법입니다.

+2

와우, 이것은 엄청난 도움이된다. 자세한 설명 주셔서 감사합니다! – Andreanna

2

문제는 하단 파일의 탭이라고 생각합니다.

while i < 500: 
    if Lines[i] == searchquery: 
     f2.write(Lines[i]) 
     f2.write(Lines[i+1]) 
     f2.write(Lines[i+2]) 
     i = i+1 
    else: 
     i = i+1 
1

ajon은 올바른 대답을 가지고 있지만 길잡이를 찾고있는 한 해결책은 파이썬이 제공 할 수있는 상위 레벨 구조를 활용하지 않습니다. 방법 :

searchquery = 'am\n' 

with open('Test.txt') as f1: 
    with open(Output.txt, 'a') as f2: 

    Lines = f1.readlines() 

    try: 
     i = Lines.index(searchquery) 
     for iline in range(i, i+3): 
     f2.write(Lines[iline]) 
    except: 
     print "not in file" 

두 개의 "with"문은 예외가 발생하더라도 자동으로 파일을 닫습니다.

with open('Test.txt') as f1: 
    with open(Output.txt, 'a') as f2: 
     for line in f1: 
     if line == searchquery: 
      f2.write(line) 
      f2.write(f1.next()) 
      f2.write(f1.next()) 
:

아직도 더 나은 솔루션은 파일 객체에 반복을 사용하는 대신, 라인별로 공정 라인에서 한 번에 전체 파일을 읽는 피하기 위해 수 (사람이 될 수 얼마나 큰 알고?)와 것

이 모든 것은 대상 라인 너머에 적어도 두 개의 추가 라인이 있다고 가정합니다.

+0

"이 모든 것은 목표 라인 너머에 적어도 두 개의 추가 라인이 있다고 가정합니다." -이 상황을 처리하는 방법은 [try ... except] 블록 (http://docs.python.org/reference/compound_stmts.html#the-try-statement)을 사용하는 것이 중요합니다. :'시도 : f2.write (f1.next()); StopIteration : pass' 또는 이와 유사한 것을 제외합니다. –

+0

맨손으로'except :'문을 사용해서는 안되며, 명시 적으로 catch하고 싶은 예외를 항상 나열해야합니다 (이 경우'IndexError'). 맨손으로'except :'를 사용하면 생각하지 못한 것을 잡을 수 있으며, 예상했던 경우와 다르게 처리해야합니다. 이 경우 예외가 발생하여 코드가 계속 진행되지 않고 코드가 중단되어 손상 될 수 있습니다. –

1

'Output.txt'이외의 파일 시스템 관련 문제가 발생하지 않도록 시도 했습니까?

진단 중에 펑키하지 못한 예상치 못한 문제를 피하는 절대 경로는 어떻습니까?

이 조언은 단순히 진단의 관점에서입니다. 또한 OS X dtrace 및 dtruss를 확인하십시오.

참조 : 대량의 데이터로 작업 할 때 라인으로 Equivalent of strace -feopen <command> on mac os X

0

쓰기 라인이 느려질 수 있습니다. 한 번에 여러 행을 읽고 쓰는 방법으로 읽기/쓰기 작업을 가속화 할 수 있습니다.

from itertools import slice 

f1 = open('Test.txt') 
f2 = open('Output.txt', 'a') 

bunch = 500 
lines = list(islice(f1, bunch)) 
f2.writelines(lines) 

f1.close() 
f2.close() 

줄이 너무 길고 시스템에 따라 500 줄을 목록에 배치하지 못할 수도 있습니다. 그렇다면 bunch 크기를 줄이고 모든 것을 기록하는 데 필요한만큼의 읽기/쓰기 단계가 있어야합니다.

관련 문제