Самый быстрый способ удалить строку из большого файла в Python - PullRequest
22 голосов
/ 24 февраля 2010

Я работаю с очень большим (~ 11 ГБ) текстовым файлом в системе Linux. Я запускаю его через программу, которая проверяет файл на наличие ошибок. Как только ошибка найдена, мне нужно либо исправить строку, либо удалить ее полностью. А потом повтори ...

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

Какой самый быстрый (с точки зрения времени выполнения) способ удалить конкретную строку из этого большого файла? Я думал сделать это на Python ... но был бы открыт для других примеров. Строка может быть в любом месте в файле.

Если Python, предположим следующий интерфейс:

def removeLine(filename, lineno):

Спасибо

Ответы [ 9 ]

13 голосов
/ 25 февраля 2010

Вы можете иметь два файловых объекта для одного и того же файла одновременно (один для чтения, другой для записи):

def removeLine(filename, lineno):
    fro = open(filename, "rb")

    current_line = 0
    while current_line < lineno:
        fro.readline()
        current_line += 1

    seekpoint = fro.tell()
    frw = open(filename, "r+b")
    frw.seek(seekpoint, 0)

    # read the line we want to discard
    fro.readline()

    # now move the rest of the lines in the file 
    # one line back 
    chars = fro.readline()
    while chars:
        frw.writelines(chars)
        chars = fro.readline()

    fro.close()
    frw.truncate()
    frw.close()
7 голосов
/ 25 февраля 2010

Измените файл вместо , поврежденная строка будет заменена пробелами, поэтому оставшаяся часть файла не будет перемещаться на диске Вы также можете " исправить " строку на месте, если исправление не длиннее строки, которую вы заменяете

import os
from mmap import mmap
def removeLine(filename, lineno):
    f=os.open(filename, os.O_RDWR)
    m=mmap(f,0)
    p=0
    for i in range(lineno-1):
        p=m.find('\n',p)+1
    q=m.find('\n',p)
    m[p:q] = ' '*(q-p)
    os.close(f)

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

1 голос
/ 25 февраля 2010

Обновление: решение с использованием sed в соответствии с просьбой автора в комментарии.

Для удаления, например, второй строки файла:

sed '2d' input.txt

Используйте переключатель -i для редактирования на месте. Предупреждение: это разрушительная операция. Прочтите справку по этой команде, чтобы узнать, как сделать резервную копию автоматически.

1 голос
/ 25 февраля 2010

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

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

Если строки имеют фиксированную длину и вы хотите удалить определенные номера строк, то вы можете использовать seek для перемещения указателя файла ... Хотя я сомневаюсь, что вам так повезло.

1 голос
/ 24 февраля 2010

Насколько я знаю, вы не можете просто открыть текстовый файл с помощью Python и удалить строку. Вы должны создать новый файл и переместить в него все, кроме этой строки. Если вы знаете конкретную строку, то вы бы сделали что-то вроде этого:

f = open('in.txt')
fo = open('out.txt','w')

ind = 1
for line in f:
    if ind != linenumtoremove:
        fo.write(line)
    ind += 1

f.close()
fo.close()

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

0 голосов
/ 04 декабря 2013

Я предоставлю две альтернативы на основе коэффициента поиска (номер строки или строка поиска):

Номер строки

def removeLine2(filename, lineNumber):
    with open(filename, 'r+') as outputFile:
        with open(filename, 'r') as inputFile:

            currentLineNumber = 0 
            while currentLineNumber < lineNumber:
                inputFile.readline()
                currentLineNumber += 1

            seekPosition = inputFile.tell()
            outputFile.seek(seekPosition, 0)

            inputFile.readline()

            currentLine = inputFile.readline()
            while currentLine:
                outputFile.writelines(currentLine)
                currentLine = inputFile.readline()

        outputFile.truncate()

Строка

def removeLine(filename, key):
    with open(filename, 'r+') as outputFile:
        with open(filename, 'r') as inputFile:
            seekPosition = 0 
            currentLine = inputFile.readline()
            while not currentLine.strip().startswith('"%s"' % key):
                seekPosition = inputFile.tell()
                currentLine = inputFile.readline()

            outputFile.seek(seekPosition, 0)

            currentLine = inputFile.readline()
            while currentLine:
                outputFile.writelines(currentLine)
                currentLine = inputFile.readline()

        outputFile.truncate()
0 голосов
/ 25 февраля 2010

@ OP, если вы можете использовать awk, например, предполагая, что номер строки равен 10

$ awk 'NR!=10' file > newfile
0 голосов
/ 25 февраля 2010

Я думаю, что был несколько похожий, если не точно такой же тип вопроса, задаваемый здесь. Чтение (и запись) построчно происходит медленно, но вы можете сразу прочитать больший кусок в память, пройти по этой строке построчно, пропуская ненужные строки, а затем записать это как отдельный кусок в новый файл. Повторите, пока не сделано. Наконец замените оригинальный файл новым файлом.

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

0 голосов
/ 25 февраля 2010
def removeLine(filename, lineno):
    in = open(filename)
    out = open(filename + ".new", "w")
    for i, l in enumerate(in, 1):
        if i != lineno:
            out.write(l)
    in.close()
    out.close()
    os.rename(filename + ".new", filename)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...