usando Python per cancellare una riga specifica in un file


145

Diciamo che ho un file di testo pieno di soprannomi. Come posso eliminare un soprannome specifico da questo file, usando Python?


1
Prova fileinputcome descritto da @ jf-sebastian qui . Sembra che ti permetta di lavorare riga per riga, tramite un file temporaneo, il tutto con una semplice forsintassi.
Kevin,

Risposte:


205

Innanzitutto, apri il file e ottieni tutte le linee dal file. Quindi riaprire il file in modalità di scrittura e riscrivere le righe, ad eccezione della riga che si desidera eliminare:

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)

È necessario strip("\n")il carattere di nuova riga nel confronto perché se il file non termina con un carattere di nuova riga line, neanche l' ultimo sarà.


2
perché dobbiamo aprirlo e chiuderlo due volte?
Ooker

3
@Ooker: devi aprire il file due volte (e chiuderlo in mezzo) perché nella prima modalità è "sola lettura" perché stai solo leggendo le righe correnti nel file. Quindi lo chiudi e lo riapri in "modalità di scrittura", dove il file è scrivibile e sostituisci il contenuto del file senza la riga che desideri rimuovere.
Devin,

4
Perché Python non ci consente di farlo in una riga?
Ooker

5
@Ooker, quando leggi una linea, prova a immaginare un cursore che si muove lungo la linea mentre viene letto. Una volta che la riga è stata letta, il cursore è ora passato. Quando provi a scrivere nel file scrivi dove si trova attualmente il cursore. Riaprendo il file si reimposta il cursore.
Waddas,

4
Usa il composto with!
Sceluswe,

101

Soluzione a questo problema con una sola apertura:

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

Questa soluzione apre il file in modalità r / w ("r +") e fa uso di seek per resettare il puntatore f quindi troncare per rimuovere tutto dopo l'ultima scrittura.


2
Questo ha funzionato molto bene per me, dato che ho dovuto usare anche lockfile (fcntl). Non sono riuscito a trovare alcun modo per utilizzare fileinput insieme a fcntl.
Easyrider,

1
Sarebbe bello vedere alcuni effetti collaterali di questa soluzione.
user1767754

3
Non lo farei. Se ricevi un errore nel forloop, finirai con un file parzialmente sovrascritto, con linee duplicate o una linea tagliata a metà. Potresti voler f.truncate()subito dopo f.seek(0). In questo modo se ricevi un errore finirai con un file incompleto. Ma la vera soluzione (se si dispone dello spazio su disco) è l'output in un file temporaneo e quindi utilizzare os.replace()o pathlib.Path(temp_filename).replace(original_filename)scambiarlo con l'originale dopo che tutto è riuscito.
Boris,

Potresti aggiungere, i.strip('\n') != "line you want to remove..."come indicato nella risposta accettata, che risolverebbe perfettamente il mio problema. Perché proprio inon ha fatto nulla per me
Mangohero1

31

L'opzione migliore e più veloce, piuttosto che archiviare tutto in un elenco e riaprire il file per scriverlo, secondo me è di riscrivere il file altrove.

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)

Questo è tutto! In un ciclo e solo uno puoi fare la stessa cosa. Sarà molto più veloce.


Invece di usare normale per il ciclo, possiamo fare uso di Generator Expression. In questo modo il programma non caricherà tutte le linee dal file alla memoria, il che non è una buona idea in caso di file di grandi dimensioni. Avrà solo una riga alla volta in memoria. Con l'espressione del generatore per loop apparirà,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde,

4
@ShriShinde Non stai nemmeno leggendo il file in memoria quando esegui il ciclo sull'oggetto file, quindi questa soluzione funziona in modo identico al tuo suggerimento.
Steinar Lima,

Potresti voler eliminare il file originale e rinominare il secondo file con il nome del file originale, che con Python su un sistema operativo Linux sarebbe simile a questo,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max

6
os.replace(novità in python v 3.3) è più multipiattaforma di una chiamata di sistema a mv.
7yl4r,

Semplice e fantastico.
JuBaer AD

27

Questo è un "fork" dalla risposta di @Lother (che credo debba essere considerata la risposta giusta).


Per un file come questo:

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

Questa forcella della soluzione di Lother funziona perfettamente:

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

miglioramenti:

  • with open, che scartano l'utilizzo di f.close()
  • più chiaro if/elseper valutare se la stringa non è presente nella riga corrente

Se è richiesto f.seek (0)?
yifan,

@yifan si. Altrimenti invece di sovrascrivere il file aggiungerai il file a se stesso (senza le righe che stai escludendo).
Boris,

5

Il problema con la lettura delle righe nel primo passaggio e le modifiche (eliminazione di righe specifiche) nel secondo passaggio è che se le dimensioni del file sono enormi, si esaurisce la RAM. Invece, un approccio migliore è leggere le righe, una per una, e scriverle in un file separato, eliminando quelle che non ti servono. Ho adottato questo approccio con file grandi come 12-50 GB e l'utilizzo della RAM rimane quasi costante. Solo i cicli della CPU mostrano l'elaborazione in corso.


2

Mi è piaciuto l'approccio fileinput come spiegato in questa risposta: eliminazione di una riga da un file di testo (python)

Ad esempio, ho un file che contiene righe vuote e voglio rimuovere righe vuote, ecco come l'ho risolto:

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

Nota: le righe vuote nel mio caso avevano lunghezza 1


2

Se usi Linux, puoi provare il seguente approccio.
Supponiamo di avere un file di testo chiamato animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Elimina la prima riga:

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

poi

$ cat animal.txt
pig
cat
monkey
elephant

7
Questa soluzione non è indipendente dal sistema operativo e poiché OP non ha specificato un sistema operativo, non c'è motivo di pubblicare una risposta specifica per Linux.
Steinar Lima,

2
Chiunque suggerisca di usare un sottoprocesso per qualsiasi cosa si possa fare con solo Python ottiene un voto negativo! E +1 a @SteinarLima ... Sono d'accordo
Jamie Lindsey il

2

Penso che se leggi il file in un elenco, quindi fai l'iterazione sull'elenco per cercare il soprannome di cui vuoi sbarazzarti. Puoi farlo in modo molto efficiente senza creare file aggiuntivi, ma dovrai riscrivere il risultato nel file di origine.

Ecco come potrei farlo:

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

Presumo nicknames.csvcontenga dati come:

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

Quindi caricare il file nell'elenco:

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

Successivamente, scorrere per elencare in modo che corrisponda agli input da eliminare:

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

Infine, riscrivi il risultato nel file:

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

In generale, non puoi; devi riscrivere l'intero file (almeno dal punto di modifica alla fine).

In alcuni casi specifici puoi fare di meglio -

se tutti i tuoi elementi di dati hanno la stessa lunghezza e in nessun ordine specifico e conosci l'offset di quello che vuoi eliminare, puoi copiare l'ultimo elemento su quello da eliminare e troncare il file prima dell'ultimo elemento ;

oppure potresti semplicemente sovrascrivere il blocco di dati con un valore 'this is bad data, skip it' o mantenere un flag 'this item was cancelled' nei tuoi elementi di dati salvati in modo da poterlo contrassegnare cancellato senza modificare in altro modo il file.

Questo è probabilmente eccessivo per i documenti brevi (qualcosa di meno di 100 KB?).


1

Probabilmente hai già una risposta corretta, ma ecco la mia. Invece di usare un elenco per raccogliere dati non filtrati (quale readlines()metodo fa), io uso due file. Uno è per contenere i dati principali e il secondo per filtrare i dati quando si elimina una stringa specifica. Ecco un codice:

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

Spero che lo troverai utile! :)


0

Salvare le righe del file in un elenco, quindi rimuovere dall'elenco la riga che si desidera eliminare e scrivere le righe rimanenti in un nuovo file

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)

Quando si dà una risposta è preferibile dare una spiegazione sul perché la tua risposta è quella.
Stephen Rauch,

Se il tuo file non termina con una nuova riga, questo codice non rimuoverà l'ultima riga anche se contiene una parola che desideri rimuovere.
Boris,

0

ecco qualche altro metodo per rimuovere una / alcune righe da un file:

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

Mi piace questo metodo usando fileinput e il metodo 'inplace':

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

È un po 'meno prolisso rispetto alle altre risposte ed è abbastanza veloce per


0

Puoi usare la rebiblioteca

Supponendo che sia possibile caricare il file txt completo. Quindi si definisce un elenco di soprannomi indesiderati e li si sostituisce con una stringa vuota "".

# 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

Per eliminare una riga specifica di un file dal suo numero di riga :

Sostituisci le variabili nomefile e line_to_delete con il nome del tuo file e il numero di riga che desideri eliminare.

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

Esempio di output :

Deleted line: 3

non è necessario costruire un dict, basta usarefor nb, line in enumerate(f.readlines())
Dionys

-3

Prendi il contenuto del file, dividerlo per newline in una tupla. Quindi, accedi al numero di riga della tua tupla, unisciti alla tua tupla risultante e sovrascrivi al file.


6
(1) vuoi dire tuple(f.read().split('\n'))?? (2) "accedi al numero di riga della tua tupla" e "unisciti alla tua tupla risultante" suona piuttosto misterioso; il codice Python effettivo potrebbe essere più comprensibile.
John Machin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.