Python обрезает строки по мере их чтения.

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

file = open('myfile.txt', 'rw+')
for line in file:
   processLine(line)
   file.truncate(line)

Это кажется простой проблемой, но я хотел бы сделать это правильно, а не много сложных вызовов seek() и tell().

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

Проведя много времени на эту проблему, я решил, что все, вероятно, правы, и это просто не очень хороший способ сделать что-то. Это просто казалось таким изящным решением. То, что я искал, было чем-то вроде FIFO, который просто дал бы мне поп-строки из файла.

Ответ 1

Удалите все строки после того, как вы покончили с ними:

with open('myfile.txt', 'rw+') as file:
    for line in file:
        processLine(line)
    file.truncate(0)

Удалить каждую строку независимо:

lines = open('myfile.txt').readlines()

for line in lines[::-1]: # process lines in reverse order
    processLine(line)
    del lines[-1]  # remove the [last] line

open('myfile.txt', 'w').writelines(lines)

Вы можете оставить только те строки, которые вызывают исключения:

import fileinput

for line in fileinput.input(['myfile.txt'], inplace=1):
    try: processLine(line)
    except Exception:
         sys.stdout.write(line) # it prints to 'myfile.txt'

В общем, как уже говорили другие люди, плохо то, что вы пытаетесь сделать.

Ответ 2

Вы не можете. Это просто невозможно с реализацией реальных текстовых файлов в текущих файловых системах.

Текстовые файлы являются последовательными, поскольку строки в текстовом файле могут иметь любую длину. Удаление определенной строки означает переписывание всего файла с этой точки.

Предположим, что у вас есть файл со следующими тремя строками:

'line1\nline2reallybig\nline3\nlast line'

Чтобы удалить вторую строку, вам нужно будет перемещать позиции третьего и четвертого строк на диске. Единственный способ - сохранить третью и четвертую строки где-нибудь, усечь файл на второй строке и переписать недостающие строки.

Если вы знаете размер каждой строки текстового файла, вы можете обрезать файл в любой позиции с помощью .truncate(line_size * line_number), но даже тогда вам придется переписать все после строки.

Ответ 3

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

try :
    for index, line in enumerate(file) :
        processLine(line)
except :
    # Failed, start from this line number next time.
    print(index)
    raise

Ответ 4

Усечение файла по мере чтения кажется немного экстремальным. Что делать, если ваш script имеет ошибку, которая не вызывает ошибку? В этом случае вам нужно перезапустить в начале файла.

Как насчет того, чтобы ваш script распечатывал номер строки, в котором он ломался, и чтобы он брал номер строки в качестве параметра, чтобы вы могли указать, с какой строки начать обработку?

Ответ 5

Прежде всего, вызов операции truncate, вероятно, не лучший выбор. Если я правильно понял проблему, вы хотите удалить все до текущей позиции в файле. (Я бы ожидал, что truncate вырезает все из текущей позиции до конца файла. Вот как работает стандартный метод Python truncate, по крайней мере, если я правильно разобрался.)

Во-вторых, я не уверен, что было бы разумно изменить файл при повторении с использованием цикла for. Не лучше ли было бы сохранить количество обработанных строк и удалить их после завершения основного цикла, исключения или нет? Итератор файлов поддерживает фильтрацию на месте, что означает, что после этого должно быть довольно просто удалить обработанные строки.

P.S. Я не знаю Питона, возьмите это с солью.

Ответ 6

У связанного сообщения есть то, что кажется хорошей стратегией для этого, см.  Как запустить первый процесс из списка процессов, хранящихся в файле, и сразу удалить первую строку, как если бы файл был в очереди, и я назвал "pop" ?

Я использовал его следующим образом:

  import os;

  tasklist_file = open(tasklist_filename, 'rw');  
  first_line = tasklist_file.readline();
  temp = os.system("sed -i -e '1d' " + tasklist_filename); # remove first line from task file;

Я не уверен, что он работает в Windows. Пробовал это на маке, и он сделал трюк.

Ответ 7

Это то, что я использую для файловых очередей. Он возвращает первую строку и переписывает файл с остальными. Когда это будет сделано, он вернет None:

def pop_a_text_line(filename):
    with open(filename,'r') as f:
        S = f.readlines()
    if len(S) > 0:
        pop = S[0]
        with open(filename,'w') as f:
            f.writelines(S[1:])
    else:
        pop = None
    return pop