Использование python для записи определенных строк из одного файла в другой файл

У меня есть ~ 200 коротких текстовых файлов (50kb), которые имеют одинаковый формат. Я хочу найти строку в каждом из этих файлов, который содержит определенную строку, а затем записать эту строку плюс следующие три строки (но не остальные строки в файле) в другой текстовый файл. Я пытаюсь научить себя python, чтобы сделать это, и написал очень простой и грубый маленький скрипт, чтобы попробовать это. Я использую версию 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') 

и не может заставить это работать. Если кто-то может объяснить, что я делаю неправильно, или предложить некоторые предложения о том, что я должен попробовать, я был бы очень благодарен. Кроме того, если у вас есть предложения по поиску лучше, я также благодарен им. Я использую тестовый файл, где строка, которую я хочу найти, является единственным текстом в строке, но в моих реальных файлах строка, которая мне нужна, все еще находится в начале строки, но за ней следует куча другого текста, поэтому Я думаю, что то, как я создал вещи сейчас, тоже не будет работать.

Спасибо, и извините, если это супер-основной вопрос!

Как отметил @ajon, я не думаю, что в коде есть что-то принципиально неправильное, кроме отступов. С исправлением отступов это работает для меня. Однако есть несколько возможностей для улучшения.

1) В Python стандартным способом итерации над вещами является использование цикла for . При использовании цикла for вам не нужно определять переменные счетчика циклов и отслеживать их самостоятельно, чтобы перебирать вещи. Вместо этого вы пишете что-то вроде этого

 for line in lines: print line 

перебирать все элементы в списке строк и печатать их.

2) В большинстве случаев это будет выглядеть for ваших циклов. Однако есть ситуации, когда вы действительно хотите отслеживать количество циклов. Ваш случай такой ситуации, потому что вам нужна не только одна строка, но и следующие три, и поэтому необходимо использовать счетчик для индексации ( lst[i] ). Для этого есть enumerate() , который вернет список элементов и их индекс, по которому вы затем можете зацикливаться.

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

Если вы должны вручную отслеживать счетчик циклов, как в вашем примере, есть две вещи:

3) что i = i+1 следует вывести из блоков if и else . Вы делаете это в обоих случаях, поэтому поставьте его после 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) Теперь это вызовет IndexError с файлами короче 500 строк. Вместо жесткого кодирования число циклов 500, вы должны использовать фактическую длину последовательности, которую вы повторяете. len(lines) даст вам эту длину. Но вместо использования цикла while используйте цикл for и range(len(lst)) чтобы перебирать список из диапазона от нуля до len(lst) - 1 .

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

5) open() может использоваться как менеджер контекста, который заботится о закрытии файлов для вас. контекстные менеджеры – довольно продвинутая концепция, но довольно просты в использовании, если они уже предоставлены для вас. Делая что-то подобное

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

файл будет открыт и доступен для вас как f внутри него with блоком. После того, как вы покинете блок, файл будет автоматически закрыт, поэтому вы не можете забыть закрыть файл.

В вашем случае вы открываете два файла. Это можно сделать, просто используя два with операторами и вложи их

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

или, в Python 2.7 / Python 3.x, путем вставки двух менеджеров контекста в одном выражении:

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

6) В зависимости от операционной системы файл был создан, окончание строк отличается. На UNIX-подобных платформах это \n , Mac до использования OS X \r , а Windows использует \r\n . Так что Lines[i] == searchquery не будут совпадать для окончаний строк в Mac или Windows. file.readline() может иметь дело со всеми тремя, но поскольку он сохраняет все концы строк в конце строки, сравнение не удастся. Это решается с помощью str.strip() , который будет str.strip() строку всех пробелов в начале и конце и сравнивать шаблон поиска без окончания строки:

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

(Чтение файла с использованием file.read() и использование str.splitlines() было бы другой альтернативой.)

Но, поскольку вы упомянули, что ваша строка поиска фактически появляется в начале строки, позволяет сделать это, используя str.startswith() :

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

7) Официальное руководство по стилю для Python, PEP8 , рекомендует использовать 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, весь этот код предполагает, что если ваша строка поиска соответствует, по крайней мере две строки следуют за ней. Если вы не можете полагаться на это предположение, дело в этом случае с помощью try...except блок, такой как @poorsod, является правильным путем.

Я думаю, что ваша проблема – вкладки нижнего файла.

Вам нужно сделать отступ, если Lines[i] до i=i+1 такие как:

 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 

ajon имеет правильный ответ, но пока вы ищете руководство, ваше решение не использует преимущества высокоуровневых конструкций, которые может предложить Python. Как насчет:

 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 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()) 

Все они предполагают, что есть не менее двух дополнительных линий за пределами вашей целевой линии.

Вы пытались использовать что-то другое, кроме «Output.txt», чтобы избежать проблем, связанных с файловой системой, в качестве проблемы?

Как насчет абсолютного пути, чтобы избежать каких-либо напуганных непредвиденных проблем при диагностировании этого.

Этот совет просто с диагностической точки зрения. Также проверьте OS X dtrace и dtruss.

См .: Эквивалент strace -feopen <command> на mac os X