Verwenden von Python zum Löschen einer bestimmten Zeile in einer Datei

145

Angenommen, ich habe eine Textdatei voller Spitznamen. Wie kann ich mit Python einen bestimmten Spitznamen aus dieser Datei löschen?

SourD
quelle
1
Versuchen Sie es fileinputwie von @ jf-sebastian hier beschrieben . Es scheint Ihnen zu ermöglichen, Zeile für Zeile über eine temporäre Datei mit einer einfachen forSyntax zu arbeiten.
Kevin

Antworten:

205

Öffnen Sie zuerst die Datei und holen Sie alle Ihre Zeilen aus der Datei. Öffnen Sie dann die Datei im Schreibmodus erneut und schreiben Sie Ihre Zeilen zurück, mit Ausnahme der Zeile, die Sie löschen möchten:

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)

Sie müssen strip("\n")das Newline-Zeichen im Vergleich verwenden, denn wenn Ihre Datei nicht mit einem Newline-Zeichen endet, wird das allerletzte lineauch nicht.

houbysoft
quelle
2
warum müssen wir es zweimal öffnen und schließen?
Ooker
3
@Ooker: Sie müssen die Datei zweimal öffnen (und dazwischen schließen), da sie im ersten Modus "schreibgeschützt" ist, weil Sie nur die aktuellen Zeilen in der Datei lesen. Sie schließen es dann und öffnen es erneut im "Schreibmodus", in dem die Datei beschreibbar ist, und Sie ersetzen den Inhalt der Datei ohne die Zeile, die Sie entfernen möchten.
Devin
4
Warum erlaubt uns Python nicht, dies in einer Zeile zu tun?
Ooker
5
@Ooker, Wenn Sie eine Zeile lesen, stellen Sie sich einen Cursor vor, der sich beim Lesen entlang der Zeile bewegt. Sobald diese Zeile gelesen wurde, ist der Cursor nun darüber hinaus. Wenn Sie versuchen, in die Datei zu schreiben, schreiben Sie, wo sich der Cursor gerade befindet. Durch erneutes Öffnen der Datei setzen Sie den Cursor zurück.
Waddas
4
Verwenden Sie die mit Verbindung!
Sceluswe
100

Lösung für dieses Problem mit nur einem einzigen offenen:

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

Diese Lösung öffnet die Datei im R / W-Modus ("r +") und verwendet die Suche, um den f-Zeiger zurückzusetzen und dann abzuschneiden, um alles nach dem letzten Schreibvorgang zu entfernen.

Lother
quelle
2
Dies funktionierte sehr gut für mich, da ich auch lockfile (fcntl) verwenden musste. Ich konnte keine Möglichkeit finden, fileinput zusammen mit fcntl zu verwenden.
Easyrider
1
Es wäre schön, einige Nebenwirkungen dieser Lösung zu sehen.
user1767754
3
Ich würde das nicht tun. Wenn Sie einen Fehler in der forSchleife erhalten, erhalten Sie eine teilweise überschriebene Datei mit doppelten Zeilen oder einer halb abgeschnittenen Zeile. Vielleicht möchten Sie stattdessen f.truncate()gleich danach f.seek(0). Auf diese Weise erhalten Sie bei einer Fehlermeldung nur eine unvollständige Datei. Die eigentliche Lösung (wenn Sie über ausreichend Speicherplatz verfügen) besteht darin, in eine temporäre Datei auszugeben und diese dann zu verwenden os.replace()oder pathlib.Path(temp_filename).replace(original_filename)mit dem Original auszutauschen, nachdem alles erfolgreich war.
Boris
Könnten Sie hinzufügen, i.strip('\n') != "line you want to remove..."wie in der akzeptierten Antwort erwähnt, das würde mein Problem perfekt lösen. Weil iich einfach nichts für mich getan habe
Mangohero1
31

Die beste und schnellste Option, anstatt alles in einer Liste zu speichern und die Datei erneut zu öffnen, um sie zu schreiben, ist meiner Meinung nach, die Datei an anderer Stelle neu zu schreiben.

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)

Das ist es! In einer Schleife und nur einer können Sie dasselbe tun. Es wird viel schneller sein.

Barnabe
quelle
Anstatt die normale for-Schleife zu verwenden, können wir den Generatorausdruck verwenden. Auf diese Weise lädt das Programm nicht alle Zeilen von der Datei in den Speicher, was bei großen Dateien keine gute Idee ist. Es wird jeweils nur eine Zeile gespeichert. Mit Generator Ausdruck für Schleife wird aussehen,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde
4
@ShriShinde Sie lesen die Datei auch nicht in den Speicher, wenn Sie das Dateiobjekt durchlaufen, daher funktioniert diese Lösung identisch mit Ihrem Vorschlag.
Steinar Lima
Vielleicht möchten Sie die Originaldatei löschen und die zweite Datei in den Namen der Originaldatei umbenennen, was mit Python unter einem Linux-Betriebssystem so aussehen würdesubprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
6
os.replace(neu in Python v 3.3) ist plattformübergreifender als ein Systemaufruf mv.
7yl4r
Einfach und großartig.
JuBaer AD
27

Dies ist eine "Abzweigung" aus der Antwort von @Lother (die meiner Meinung nach als die richtige Antwort angesehen werden sollte).


Für eine Datei wie diese:

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

Diese Gabel aus Lothers Lösung funktioniert einwandfrei:

#!/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()

Verbesserungen:

  • with open, die die Verwendung von verwerfen f.close()
  • Klarer if/elsefür die Bewertung, ob die Zeichenfolge in der aktuellen Zeile nicht vorhanden ist
ivanleoncz
quelle
Wenn f.seek (0) erforderlich ist?
Yifan
@ Yifan ja. Andernfalls hängen Sie die Datei an sich selbst an, anstatt sie zu überschreiben (ohne die Zeilen, die Sie ausschließen).
Boris
5

Das Problem beim Lesen von Zeilen im ersten Durchgang und beim Vornehmen von Änderungen (Löschen bestimmter Zeilen) im zweiten Durchgang besteht darin, dass Ihnen bei großen Dateigrößen der Arbeitsspeicher ausgeht. Stattdessen ist es besser, die Zeilen einzeln zu lesen und in eine separate Datei zu schreiben, um die nicht benötigten zu entfernen. Ich habe diesen Ansatz mit Dateien mit einer Größe von 12 bis 50 GB ausgeführt, und die RAM-Auslastung bleibt nahezu konstant. Nur CPU-Zyklen zeigen die laufende Verarbeitung an.

Kingz
quelle
2

Ich mochte den Dateieingabeansatz, wie in dieser Antwort erläutert: Löschen einer Zeile aus einer Textdatei (Python)

Angenommen, ich habe eine Datei mit leeren Zeilen und möchte leere Zeilen entfernen. So habe ich sie gelöst:

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

Hinweis: Die Leerzeilen in meinem Fall hatten die Länge 1

Tief
quelle
2

Wenn Sie Linux verwenden, können Sie den folgenden Ansatz ausprobieren.
Angenommen, Sie haben eine Textdatei mit dem Namen animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Löschen Sie die erste Zeile:

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

dann

$ cat animal.txt
pig
cat
monkey
elephant
Ren
quelle
7
Diese Lösung ist nicht betriebssystemunabhängig, und da OP kein Betriebssystem angegeben hat, gibt es keinen Grund, eine Linux-spezifische Antwort imo zu veröffentlichen.
Steinar Lima
2
Jeder, der vorschlägt, einen Unterprozess für alles zu verwenden, was nur mit Python möglich ist, erhält eine Abwertung! Und +1 an @SteinarLima ... Ich stimme zu
Jamie Lindsey
2

Ich denke, wenn Sie die Datei in eine Liste einlesen, können Sie die Liste durchlaufen, um nach dem Spitznamen zu suchen, den Sie entfernen möchten. Sie können dies sehr effizient tun, ohne zusätzliche Dateien zu erstellen, aber Sie müssen das Ergebnis in die Quelldatei zurückschreiben.

So könnte ich das machen:

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

Ich gehe davon aus, nicknames.csventhält Daten wie:

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

Laden Sie dann die Datei in die Liste:

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

Als nächstes iterieren Sie zur Liste, um Ihren zu löschenden Eingaben zu entsprechen:

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

Zuletzt schreiben Sie das Ergebnis zurück in die Datei:

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()
Ein Malik
quelle
1

Im Allgemeinen können Sie nicht; Sie müssen die gesamte Datei erneut schreiben (zumindest vom Zeitpunkt der Änderung bis zum Ende).

In bestimmten Fällen können Sie es besser machen -

Wenn alle Ihre Datenelemente dieselbe Länge und keine bestimmte Reihenfolge haben und Sie den Versatz des Elements kennen, das Sie entfernen möchten, können Sie das letzte Element über das zu löschende Element kopieren und die Datei vor dem letzten Element abschneiden ;;

Oder Sie können den Datenblock einfach mit dem Wert "Dies sind fehlerhafte Daten, überspringen Sie ihn" überschreiben oder das Flag "Dieses Element wurde gelöscht" in Ihren gespeicherten Datenelementen beibehalten, sodass Sie es als gelöscht markieren können, ohne die Datei anderweitig zu ändern.

Dies ist wahrscheinlich ein Overkill für kurze Dokumente (alles unter 100 KB?).

Hugh Bothwell
quelle
1

Wahrscheinlich haben Sie bereits eine richtige Antwort erhalten, aber hier ist meine. Anstatt eine Liste zum Sammeln ungefilterter Daten zu verwenden (welche readlines()Methode funktioniert), verwende ich zwei Dateien. Eine dient zum Halten von Hauptdaten und die zweite zum Filtern der Daten, wenn Sie eine bestimmte Zeichenfolge löschen. Hier ist ein Code:

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

Ich hoffe, Sie finden das nützlich! :) :)

andrii1986
quelle
0

Speichern Sie die Dateizeilen in einer Liste, entfernen Sie dann die zu löschende Zeile aus der Liste und schreiben Sie die verbleibenden Zeilen in eine neue Datei

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)
Henrique Andrade
quelle
Wenn Sie eine Antwort geben, ist es vorzuziehen, eine Erklärung zu geben , WARUM Ihre Antwort diejenige ist.
Stephen Rauch
Wenn Ihre Datei nicht mit einer neuen Zeile endet, entfernt dieser Code die letzte Zeile nicht, selbst wenn er ein Wort enthält, das Sie entfernen möchten.
Boris
0

Hier ist eine andere Methode, um eine / mehrere Zeile (n) aus einer Datei zu entfernen:

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
quelle
0

Ich mag diese Methode mit fileinput und der 'inplace'-Methode:

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

Es ist etwas weniger wortreich als die anderen Antworten und schnell genug dafür

Ru887321
quelle
0

Sie können die reBibliothek verwenden

Angenommen, Sie können Ihre vollständige txt-Datei laden. Anschließend definieren Sie eine Liste unerwünschter Spitznamen und ersetzen diese durch eine leere Zeichenfolge "".

# 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
quelle
-1

So löschen Sie eine bestimmte Zeile einer Datei anhand ihrer Zeilennummer :

Ersetzen Sie die Variablen Dateiname und line_to_delete durch den Namen Ihrer Datei und die Zeilennummer, die Sie löschen möchten.

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

Beispiel Ausgabe :

Deleted line: 3
Aram Maliachi
quelle
Es ist nicht nötig, ein Diktat zu erstellen. Verwenden Sie einfachfor nb, line in enumerate(f.readlines())
Dionys
-3

Nehmen Sie den Inhalt der Datei und teilen Sie ihn durch Zeilenumbruch in ein Tupel auf. Greifen Sie dann auf die Zeilennummer Ihres Tupels zu, verbinden Sie Ihr Ergebnistupel und überschreiben Sie die Datei.

Nikhil
quelle
6
(1) meinst du tuple(f.read().split('\n'))? (2) "Zugriff auf die Zeilennummer Ihres Tupels" und "Join your result tuple" klingen ziemlich mysteriös; Der tatsächliche Python-Code ist möglicherweise verständlicher.
John Machin