Печатать определенные строки из нескольких файлов в Python

У меня есть 30 текстовых файлов по 30 строк в каждом. По какой-то причине мне нужно написать скрипт, который открывает файл 1, печатает строку 1 файла 1, закрывает его, открывает файл 2, печатает строку 2 файла 2, закрывает его и так далее. Я попробовал это:

import glob

files = glob.glob('/Users/path/to/*/files.txt')             
for file in files:
    i = 0
    while i < 30:
        with open(file,'r') as f:
            for index, line in enumerate(f):
                if index == i:
                    print(line)
                    i += 1
                    f.close()
            continue 

Очевидно, я получил следующую ошибку:

ValueError: операция ввода-вывода для закрытого файла.

Из-за f.close(). Как я могу сделать, чтобы перейти от файла к следующему после чтения только нужной строки?

4 ответа

Прежде всего, чтобы ответить на вопрос, как отмечено в комментариях, ваша основная проблема заключается в том, что вы закрываете файл, а затем пытаетесь продолжить его повторение. Виновный код:

        for index, line in enumerate(f): # <-- Reads
            if index == i:
                print(line)
                i += 1
                f.close()                # <-- Closes when you get a hit
                                         # But loop is not terminated, so you'll loop again

Самое простое решение - просто break вместо того, чтобы явно закрывать, так как ваш with оператор уже гарантирует детерминированное закрытие при выходе из блока:

        for index, line in enumerate(f):
            if index == i:
                print(line)
                i += 1
                break

Но так как это было забавно, вот немного очищенного кода для выполнения той же задачи:

import glob
from itertools import islice

# May as well use iglob since we'll stop processing at 30 files anyway    
files = glob.iglob('/Users/path/to/*/files.txt')

# Stop after no more than 30 files, use enumerate to track file num
for i, file in enumerate(islice(files, 30)):
    with open(file,'r') as f:
        # Skip the first i lines of the file, then print the next line
        print(next(islice(f, i, None)))

Вы можете использовать linecache Модуль, чтобы получить нужную вам линию и избавить себя от головной боли:

import glob
import linecache

line = 1
for file in glob.glob('/Users/path/to/*/files.txt'):
    print(linecache.getline(file, line))
    line += 1
    if line > 30:  # if you really need to limit it to only 30
        break

Разделите свою работу на более простые шаги, пока последний шаг не станет тривиальным. Используйте функции.

Помните, что файловый объект работает как последовательность строк.

def nth(n, sequence):
  for position, item in enumerate(sequence):
    if position == n:
      return item
  return None  # if the sequence ended before position n

def printNthLines(glob_pattern)
  # Note: sort file names; glob guarantees no order.
  filenames = sorted(glob.glob(glob_pattern))
  for position, filename in enumerate(filenames):
    with open(filename) as f:
      line = nth(position, f)  # Pick the n-th line.
      if line is not None:
        print(line)
      # IDK what to do if there's no n-th line in n-th file

printNthLines('path/to/*/file.txt')

Очевидно, что мы сканируем n-й файл в n-ю строку, но это неизбежно, нет способа напрямую перейти к n-й строке в текстовом файле.

Я думаю, что-то вроде этого, что вы хотите:

import glob

files = glob.glob('/Users/path/to/*/files.txt')             
for file in files:
    i = 0
    while i < 30:
        with open(file,'r') as f:
            for index, line in enumerate(f):
                if index == i:
                    print(line)
                    i += 1
                    break
        f.close()

В настоящее время вы закрываете файл в середине цикла for, а затем снова пытаетесь его прочитать. Так что если вы закрываете файл только после выхода из цикла for, все будет в порядке.

Другие вопросы по тегам