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


145

Скажімо, у мене текстовий файл, повний ніків. Як я можу видалити певний псевдонім із цього файлу за допомогою Python?


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також не буде.


2
чому ми повинні відкривати та закривати її двічі?
Ooker

3
@Ooker: Вам потрібно відкрити файл двічі (і закрити його між ними), оскільки в першому режимі він "лише для читання", оскільки ви просто читаєте в поточних рядках у файлі. Потім ви закриваєте його і знову відкриваєте його в режимі "запису", де файл можна записати, і ви замінюєте вміст файлу, на який ви хочете видалити рядок.
Девін

4
Чому Python не дозволяє нам це робити в один рядок?
Ooker

5
@Ooker. Коли ви читаєте рядок, спробуйте уявити курсор, що рухається по лінії, коли він читається. Після того, як цей рядок було прочитано, курсор тепер пройшов повз нього. Коли ви намагаєтеся записати у файл, ви пишете, де знаходиться курсор на даний момент. Повторним відкриттям файлу ви скинете курсор.
Waddas

4
Використовуйте зі складом!
Sceluswe

100

Вирішити цю проблему можна лише одним відкритим:

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-pointer, а потім усікати, щоб видалити все після останнього запису.


2
Це дуже добре спрацювало для мене, оскільки мені довелося використовувати і lockfile (fcntl). Я не міг знайти жодного способу використання fileinput разом з fcntl.
Easyrider

1
Було б непогано побачити деякі побічні ефекти цього рішення.
користувач1767754

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)

Це воно! В одній петлі і в одній тільки ви можете зробити те ж саме. Це буде набагато швидше.


Замість використання нормального для циклу ми можемо скористатись генераторною експресією. Таким чином програма не завантажить усі рядки з файлу в пам'ять, що не є гарною ідеєю у випадку великих файлів. Він буде мати лише один рядок у пам'яті за раз. З генераторним виразом для циклу буде виглядати так,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShinde Ви також не читаєте файл у пам’яті під час циклічного перегляду об’єкта файлу, тому це рішення працює ідентично вашій пропозиції.
Стейнар Ліма

Ви можете видалити оригінальний файл і перейменувати другий файл на ім'я оригінального файлу, що з Python на ОС Linux виглядатиме так,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Макс

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

Ця вилка з рішення Lother прекрасно працює:

#!/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для оцінки, якщо рядок відсутній у поточному рядку

Якщо потрібен f.seek (0)?
yifan

@yifan так. Інакше замість того, щоб перезаписати файл, ви додасте файл до себе (без рядків, які ви виключаєте).
Борис

5

Проблема з читанням рядків у першому проході та внесенням змін (видаленням конкретних рядків) у другому проході полягає в тому, що якщо розміри файлів величезні, у вас залишиться оперативна пам'ять. Натомість, кращим підходом є читання рядків по черзі та записування їх в окремий файл, усунення тих, які вам не потрібні. Я застосував такий підхід з файлами розміром 12-50 ГБ, і використання оперативної пам’яті залишається майже постійним. Лише цикли процесора показують процес обробки.


2

Мені сподобався файловий підхід, як пояснено у цій відповіді: Видалення рядка з текстового файлу (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

7
Це рішення не є агностичним ОС, і оскільки в ОП не було вказано операційну систему, немає ніяких причин розміщувати специфічну відповідь на імму для Linux.
Стейнар Ліма

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

Сподіваюся, ви знайдете це корисним! :)


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

0

Мені подобається цей метод, використовуючи fileinput та метод 'inplace':

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

Це трохи менш багатослівно, ніж інші відповіді, і досить швидко


0

Ви можете використовувати reбібліотеку

Якщо припустити, що ви можете завантажити повний txt-файл. Потім ви визначаєте список небажаних прізвиськ і замінюєте їх порожнім рядком "".

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

-1

Щоб видалити певний рядок файлу за його номером рядка :

Замініть ім'я файлу змінних та 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 може бути зрозумілішим.
Джон Махін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.