использование Python для удаления определенной строки в файле

145

Допустим, у меня есть текстовый файл, полный псевдонимов. Как я могу удалить определенный ник из этого файла, используя Python?

Sourd
источник
1
Попробуйте , fileinputкак описано @ JF-Себастьяне здесь . Похоже, что вы можете работать построчно, через временный файл, с простым forсинтаксисом.
Кевин

Ответы:

205

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

with open("yourfile.txt", "r") as f:
    lines = f.readlines()
with open("yourfile.txt", "w") as f:
    for line in lines:
        if line.strip("\n") != "nickname_to_delete":
            f.write(line)

Вам нужно strip("\n")использовать символ новой строки в сравнении, потому что если ваш файл не заканчивается символом новой строки, то и последний lineне будет.

houbysoft
источник
2
почему мы должны открывать и закрывать его дважды?
Ooker
3
@Ooker: Вы должны открыть файл дважды (и закрыть его между ними), потому что в первом режиме он «только для чтения», потому что вы просто читаете текущие строки в файле. Затем вы закрываете его и снова открываете в «режиме записи», где файл доступен для записи, и вы заменяете содержимое файла без строки, которую хотите удалить.
Девин
4
Почему Python не позволяет нам сделать это в одну строку?
Ooker
5
@Ooker, когда вы читаете строку, попробуйте представить курсор, движущийся вдоль строки, когда она читается. Как только эта строка была прочитана, курсор теперь проходит мимо нее. Когда вы пытаетесь записать в файл, вы пишете, где курсор находится в данный момент. При повторном открытии файла вы сбрасываете курсор.
Ваддас
4
Пьют с соединением!
Sceluswe 20.09.16
101

Решение этой проблемы только с одним открытием:

with open("target.txt", "r+") as f:
    d = f.readlines()
    f.seek(0)
    for i in d:
        if i != "line you want to remove...":
            f.write(i)
    f.truncate()

Это решение открывает файл в режиме r / w («r +») и использует поиск, чтобы сбросить f-указатель, а затем усечь, чтобы удалить все после последней записи.

Lother
источник
2
Это сработало очень хорошо для меня, так как я должен был также использовать lockfile (fcntl). Я не мог найти способ использовать fileinput вместе с fcntl.
Easyrider
1
Было бы неплохо увидеть некоторые побочные эффекты этого решения.
user1767754
3
Я бы не стал этого делать. Если вы получите ошибку в forцикле, вы получите частично перезаписанный файл с дублирующимися линиями или половиной обрезанной строки. Возможно, вы захотите f.truncate()сразу после f.seek(0)этого. Таким образом, если вы получите ошибку, вы просто получите неполный файл. Но реальное решение (если у вас есть место на диске) - вывести во временный файл, а затем использовать os.replace()или pathlib.Path(temp_filename).replace(original_filename)заменить его оригиналом после того, как все будет успешно.
Борис
Можете ли вы добавить, i.strip('\n') != "line you want to remove..."как указано в принятом ответе, это прекрасно решило бы мою проблему. Потому что просто iничего не сделал для меня
Mangohero1
31

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

with open("yourfile.txt", "r") as input:
    with open("newfile.txt", "w") as output: 
        for line in input:
            if line.strip("\n") != "nickname_to_delete":
                output.write(line)

Это оно! В одном цикле и только один вы можете сделать то же самое. Это будет намного быстрее.

Barnabe
источник
Вместо использования обычного цикла for мы можем использовать выражение генератора. Таким образом, программа не будет загружать все строки из файла в память, что не является хорошей идеей в случае больших файлов. Он будет иметь только одну строку в памяти одновременно. С генератором выражение для цикла будет выглядеть так:(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde 25.02.16
4
@ShriShinde Вы не читаете файл в память, когда зацикливаетесь на объекте файла, поэтому это решение работает идентично вашему предложению.
Стейнар Лима
Возможно, вы захотите удалить исходный файл и переименовать второй файл с именем исходного файла, которое с Python на ОС Linux будет выглядеть следующим образом,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
6
os.replace(новинка в python v 3.3) более кроссплатформенная, чем системный вызов mv.
7yl4r
Просто и замечательно.
JuBaer AD
27

Это "вилка" от @Lother (который, я считаю, следует считать правильным ответом).


Для такого файла:

$ cat file.txt 
1: october rust
2: november rain
3: december snow

Эта вилка из решения Лотера отлично работает:

#!/usr/bin/python3.4

with open("file.txt","r+") as f:
    new_f = f.readlines()
    f.seek(0)
    for line in new_f:
        if "snow" not in line:
            f.write(line)
    f.truncate()

Улучшения:

  • with open, которые отказываются от использования f.close()
  • более понятным if/elseдля оценки, если строка не присутствует в текущей строке
ivanleoncz
источник
Если f.seek (0) требуется?
Ифань
@ Да, да. В противном случае вместо перезаписи файла вы добавляете файл к себе (без исключаемых строк).
Борис
5

Проблема с чтением строк на первом проходе и внесением изменений (удалением определенных строк) на втором проходе заключается в том, что если у вас большие размеры файлов, вам не хватит ОЗУ. Вместо этого, лучший подход - читать строки по одной и записывать их в отдельный файл, исключая ненужные. Я использовал этот подход для файлов размером 12-50 ГБ, и использование оперативной памяти остается практически постоянным. Только циклы процессора показывают обработку в процессе.

Kingz
источник
2

Мне понравился подход fileinput, как объяснено в этом ответе: удаление строки из текстового файла (python)

Например, у меня есть файл с пустыми строками, и я хочу удалить пустые строки, вот как я решил это:

import fileinput
import sys
for line_number, line in enumerate(fileinput.input('file1.txt', inplace=1)):
    if len(line) > 1:
            sys.stdout.write(line)

Примечание: пустые строки в моем случае имели длину 1

глубоко
источник
2

Если вы используете Linux, вы можете попробовать следующий подход.
Предположим, у вас есть текстовый файл с именем animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Удалить первую строку:

>>> import subprocess
>>> subprocess.call(['sed','-i','/.*dog.*/d','animal.txt']) 

затем

$ cat animal.txt
pig
cat
monkey
elephant
Ren
источник
7
Это решение не зависит от ОС, и, поскольку OP не указывал операционную систему, нет никаких причин размещать специфический для Linux ответ imo.
Стейнар Лима
2
Любой, кто предлагает использовать подпроцесс для всего, что может быть сделано с помощью всего лишь python, получает отрицательный ответ! И +1 к @SteinarLima ... Я согласен
Джейми Линдси
2

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

Вот как я могу это сделать:

import, os, csv # and other imports you need
nicknames_to_delete = ['Nick', 'Stephen', 'Mark']

Я предполагаю, nicknames.csvсодержит данные, такие как:

Nick
Maria
James
Chris
Mario
Stephen
Isabella
Ahmed
Julia
Mark
...

Затем загрузите файл в список:

 nicknames = None
 with open("nicknames.csv") as sourceFile:
     nicknames = sourceFile.read().splitlines()

Далее, переходим к списку, чтобы соответствовать вашим входам для удаления:

for nick in nicknames_to_delete:
     try:
         if nick in nicknames:
             nicknames.pop(nicknames.index(nick))
         else:
             print(nick + " is not found in the file")
     except ValueError:
         pass

Наконец, запишите результат обратно в файл:

with open("nicknames.csv", "a") as nicknamesFile:
    nicknamesFile.seek(0)
    nicknamesFile.truncate()
    nicknamesWriter = csv.writer(nicknamesFile)
    for name in nicknames:
        nicknamesWriter.writeRow([str(name)])
nicknamesFile.close()
Малик
источник
1

В общем, вы не можете; Вы должны написать весь файл снова (по крайней мере, с момента изменения до конца).

В некоторых конкретных случаях вы можете сделать лучше, чем это -

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

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

Это, вероятно, излишне для коротких документов (что-нибудь под 100 КБ?).

Хью Ботвелл
источник
1

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

main_file = open('data_base.txt').read()    # your main dataBase file
filter_file = open('filter_base.txt', 'w')
filter_file.write(main_file)
filter_file.close()
main_file = open('data_base.txt', 'w')
for line in open('filter_base'):
    if 'your data to delete' not in line:    # remove a specific string
        main_file.write(line)                # put all strings back to your db except deleted
    else: pass
main_file.close()

Надеюсь, вы найдете это полезным! :)

andrii1986
источник
0

Сохраните строки файла в списке, затем удалите из списка строку, которую хотите удалить, и запишите оставшиеся строки в новый файл.

with open("file_name.txt", "r") as f:
    lines = f.readlines() 
    lines.remove("Line you want to delete\n")
    with open("new_file.txt", "w") as new_f:
        for line in lines:        
            new_f.write(line)
Энрике Андраде
источник
Когда вы даете ответ, желательно дать какое-то объяснение, ПОЧЕМУ ваш ответ тот.
Стивен Раух
Если ваш файл не заканчивается новой строкой, этот код не удалит последнюю строку, даже если он содержит слово, которое вы хотите удалить.
Борис
0

Вот еще один способ удалить некоторые строки из файла:

src_file = zzzz.txt
f = open(src_file, "r")
contents = f.readlines()
f.close()

contents.pop(idx) # remove the line item from list, by line number, starts from 0

f = open(src_file, "w")
contents = "".join(contents)
f.write(contents)
f.close()
ungalcrys
источник
0

Мне нравится этот метод, использующий fileinput и метод inplace:

import fileinput
for line in fileinput.input(fname, inplace =1):
    line = line.strip()
    if not 'UnwantedWord' in line:
        print(line)

Это немного менее многословно, чем другие ответы и достаточно быстро для

Ru887321
источник
0

Вы можете использовать reбиблиотеку

Предполагая, что вы можете загрузить свой полный текстовый файл. Затем вы определяете список нежелательных псевдонимов, а затем заменяете их пустой строкой "".

# Delete unwanted characters
import re

# Read, then decode for py2 compat.
path_to_file = 'data/nicknames.txt'
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# Define unwanted nicknames and substitute them
unwanted_nickname_list = ['SourDough']
text = re.sub("|".join(unwanted_nickname_list), "", text)
MRK
источник
-1

Чтобы удалить конкретную строку файла по номеру строки :

Замените переменные filename и line_to_delete на имя вашего файла и номер строки, которую вы хотите удалить.

filename = 'foo.txt'
line_to_delete = 3
initial_line = 1
file_lines = {}

with open(filename) as f:
    content = f.readlines() 

for line in content:
    file_lines[initial_line] = line.strip()
    initial_line += 1

f = open(filename, "w")
for line_number, line_content in file_lines.items():
    if line_number != line_to_delete:
        f.write('{}\n'.format(line_content))

f.close()
print('Deleted line: {}'.format(line_to_delete))

Пример вывода :

Deleted line: 3
Арам Малиачи
источник
нет необходимости строить диктовку, просто используйтеfor nb, line in enumerate(f.readlines())
Дионис
-3

Возьмите содержимое файла, разбейте его на новую строку в кортеж. Затем получите доступ к номеру строки вашего кортежа, присоедините его к кортежу результата и перезапишите файл.

Нихилу
источник
6
(1) ты имеешь в виду tuple(f.read().split('\n'))?? (2) «получить доступ к номеру строки вашего кортежа» и «присоединиться к вашему кортежу результата» звучит довольно загадочно; Фактический код Python может быть более понятным.
Джон Мачин