utiliser Python pour supprimer une ligne spécifique dans un fichier

145

Disons que j'ai un fichier texte plein de surnoms. Comment puis-je supprimer un surnom spécifique de ce fichier, en utilisant Python?

SourD
la source
1
Essayez fileinputcomme décrit par @ jf-sebastian ici . Il semble vous permettre de travailler ligne par ligne, via un fichier temporaire, le tout avec une forsyntaxe simple .
Kevin le

Réponses:

205

Tout d'abord, ouvrez le fichier et récupérez toutes vos lignes du fichier. Ensuite, rouvrez le fichier en mode écriture et réécrivez vos lignes, à l'exception de la ligne que vous souhaitez supprimer:

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)

Vous avez besoin strip("\n")du caractère de nouvelle ligne dans la comparaison car si votre fichier ne se termine pas par un caractère de nouvelle ligne, le tout dernier linene le sera pas non plus.

houbysoft
la source
2
pourquoi devons-nous l'ouvrir et le fermer deux fois?
Ooker
3
@Ooker: Vous devez ouvrir le fichier deux fois (et le fermer entre les deux) car dans le premier mode, il est en "lecture seule" car vous ne faites que lire les lignes actuelles du fichier. Vous le fermez ensuite et le rouvrez en "mode écriture", où le fichier est inscriptible et vous remplacez le contenu du fichier sans la ligne que vous vouliez supprimer.
Devin
4
Pourquoi Python ne nous permet-il pas de faire cela en une seule ligne?
Ooker
5
@Ooker, lorsque vous lisez une ligne, essayez d'imaginer un curseur se déplaçant le long de la ligne pendant sa lecture. Une fois que cette ligne a été lue, le curseur la dépasse. Lorsque vous essayez d'écrire dans le fichier, vous écrivez là où se trouve actuellement le curseur. En rouvrant le fichier, vous réinitialisez le curseur.
Waddas
4
Utilisez le avec composé!
Sceluswe
101

Solution à ce problème avec une seule ouverture:

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

Cette solution ouvre le fichier en mode r / w ("r +") et utilise seek pour réinitialiser le pointeur f puis tronquer pour tout supprimer après la dernière écriture.

Lother
la source
2
Cela a très bien fonctionné pour moi, car je devais également utiliser lockfile (fcntl). Je n'ai trouvé aucun moyen d'utiliser fileinput avec fcntl.
Easyrider
1
Ce serait bien de voir certains effets secondaires de cette solution.
user1767754
3
Je ne ferais pas ça. Si vous obtenez une erreur dans la forboucle, vous vous retrouverez avec un fichier partiellement écrasé, avec des lignes en double ou une ligne à moitié coupée. Vous voudrez peut-être f.truncate()juste après à la f.seek(0)place. De cette façon, si vous obtenez une erreur, vous vous retrouverez avec un fichier incomplet. Mais la vraie solution (si vous avez l'espace disque) est de sortir dans un fichier temporaire puis de l'utiliser os.replace()ou pathlib.Path(temp_filename).replace(original_filename)de l'échanger avec l'original une fois que tout a réussi.
Boris
Pouvez-vous ajouter i.strip('\n') != "line you want to remove..."comme mentionné dans la réponse acceptée, cela résoudrait parfaitement mon problème. Parce que je in'ai rien fait pour moi
Mangohero1
31

La meilleure et la plus rapide option, plutôt que de tout stocker dans une liste et de rouvrir le fichier pour l'écrire, est à mon avis de réécrire le fichier ailleurs.

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)

C'est tout! Dans une boucle et une seule, vous pouvez faire la même chose. Ce sera beaucoup plus rapide.

Barnabe
la source
Au lieu d'utiliser la boucle for normale, nous pouvons utiliser l' expression du générateur. De cette façon, le programme ne chargera pas toutes les lignes du fichier vers la mémoire, ce qui n'est pas une bonne idée dans le cas de gros fichiers. Il n'aura qu'une seule ligne en mémoire à la fois. Avec l'expression du générateur, la boucle ressemblera à,(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde
4
@ShriShinde Vous ne lisez pas non plus le fichier en mémoire lors de la boucle sur l'objet fichier, donc cette solution fonctionne de la même manière que votre suggestion.
Steinar Lima le
Vous voudrez peut-être supprimer le fichier d'origine et renommer le deuxième fichier avec le nom du fichier d'origine, qui avec Python sur un système d'exploitation Linux ressemblerait à ceci,subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
Max
6
os.replace(nouveau dans python v 3.3) est plus multiplateforme qu'un appel système mv.
7yl4r
Simple et génial.
JuBaer AD
27

C'est une "fourchette" de la réponse de @Lother (qui, je crois, devrait être considérée comme la bonne réponse).


Pour un fichier comme celui-ci:

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

Cette fourchette de la solution de Lother fonctionne bien:

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

Améliorations:

  • with open, qui rejette l'utilisation de f.close()
  • plus clair if/elsepour évaluer si la chaîne n'est pas présente dans la ligne courante
Ivanleoncz
la source
Si f.seek (0) est requis?
yifan
@yifan oui. Sinon, au lieu d'écraser le fichier, vous ajouterez le fichier à lui-même (sans les lignes que vous excluez).
Boris
5

Le problème avec la lecture des lignes au premier passage et les modifications (suppression de lignes spécifiques) au second passage est que si la taille de vos fichiers est énorme, vous manquerez de RAM. Au lieu de cela, une meilleure approche consiste à lire les lignes, une par une, et à les écrire dans un fichier séparé, en éliminant celles dont vous n'avez pas besoin. J'ai utilisé cette approche avec des fichiers de 12 à 50 Go et l'utilisation de la RAM reste presque constante. Seuls les cycles CPU indiquent le traitement en cours.

Kingz
la source
2

J'ai aimé l'approche d'entrée de fichier comme expliqué dans cette réponse: Suppression d'une ligne d'un fichier texte (python)

Disons par exemple que j'ai un fichier contenant des lignes vides et que je souhaite supprimer les lignes vides, voici comment je l'ai résolu:

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

Remarque: les lignes vides dans mon cas avaient la longueur 1

Profond
la source
2

Si vous utilisez Linux, vous pouvez essayer l'approche suivante.
Supposons que vous ayez un fichier texte nommé animal.txt:

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

Supprimez la première ligne:

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

puis

$ cat animal.txt
pig
cat
monkey
elephant
Ren
la source
7
Cette solution n'est pas indépendante du système d'exploitation, et comme OP n'a pas spécifié de système d'exploitation, il n'y a aucune raison de publier une réponse spécifique à Linux imo.
Steinar Lima
2
Quiconque suggère d'utiliser un sous-processus pour tout ce qui peut être fait avec juste python obtient un vote négatif! Et +1 à @SteinarLima ... Je suis d'accord
Jamie Lindsey
2

Je pense que si vous lisez le fichier dans une liste, vous pouvez parcourir la liste pour rechercher le surnom dont vous souhaitez vous débarrasser. Vous pouvez le faire beaucoup plus efficacement sans créer de fichiers supplémentaires, mais vous devrez réécrire le résultat dans le fichier source.

Voici comment je pourrais faire ceci:

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

Je suppose que nicknames.csvcontient des données telles que:

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

Ensuite, chargez le fichier dans la liste:

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

Ensuite, parcourez la liste pour faire correspondre vos entrées à supprimer:

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

Enfin, écrivez le résultat dans le fichier:

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()
Un Malik
la source
1

En général, vous ne pouvez pas; vous devez réécrire le fichier entier (au moins du point de modification à la fin).

Dans certains cas spécifiques, vous pouvez faire mieux que cela -

si tous vos éléments de données sont de la même longueur et dans aucun ordre spécifique, et que vous connaissez le décalage de celui dont vous souhaitez vous débarrasser, vous pouvez copier le dernier élément sur celui à supprimer et tronquer le fichier avant le dernier élément ;

ou vous pouvez simplement écraser le bloc de données avec une valeur `` ce sont des données incorrectes, les ignorer '' ou conserver un indicateur `` cet élément a été supprimé '' dans vos éléments de données enregistrés de sorte que vous puissiez le marquer comme supprimé sans autrement modifier le fichier.

C'est probablement exagéré pour les documents courts (quelque chose de moins de 100 Ko?).

Hugh Bothwell
la source
1

Vous avez probablement déjà une bonne réponse, mais voici la mienne. Au lieu d'utiliser une liste pour collecter des données non filtrées (quelle readlines()méthode fait), j'utilise deux fichiers. Le premier est destiné à contenir les données principales et le second à filtrer les données lorsque vous supprimez une chaîne spécifique. Voici un 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()

J'espère que vous trouverez cela utile! :)

andrii1986
la source
0

Enregistrez les lignes de fichier dans une liste, puis supprimez de la liste la ligne que vous souhaitez supprimer et écrivez les lignes restantes dans un nouveau fichier

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
la source
Lorsque vous donnez une réponse, il est préférable d' expliquer pourquoi votre réponse est la bonne.
Stephen Rauch
Si votre fichier ne se termine pas par une nouvelle ligne, ce code ne supprimera pas la dernière ligne même si elle contient un mot que vous souhaitez supprimer.
Boris
0

voici une autre méthode pour supprimer une / des ligne (s) d'un fichier:

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
la source
0

J'aime cette méthode utilisant fileinput et la méthode 'inplace':

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

C'est un peu moins verbeux que les autres réponses et c'est assez rapide pour

Ru887321
la source
0

Vous pouvez utiliser la rebibliothèque

En supposant que vous puissiez charger votre fichier txt complet. Vous définissez ensuite une liste de surnoms indésirables, puis vous les remplacez par une chaîne vide "".

# 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
la source
-1

Pour supprimer une ligne spécifique d'un fichier par son numéro de ligne :

Remplacez les variables filename et line_to_delete par le nom de votre fichier et le numéro de ligne que vous souhaitez supprimer.

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

Exemple de sortie :

Deleted line: 3
Aram Maliachi
la source
il n'est pas nécessaire de créer un dict, il suffit d'utiliserfor nb, line in enumerate(f.readlines())
Dionys
-3

Prenez le contenu du fichier, divisez-le par nouvelle ligne en un tuple. Ensuite, accédez au numéro de ligne de votre tuple, joignez votre tuple de résultat et écrasez-le dans le fichier.

Nikhil
la source
6
(1) voulez-vous dire tuple(f.read().split('\n'))?? (2) "accéder au numéro de ligne de votre tuple" et "joindre votre tuple résultat" semblent plutôt mystérieux; le code Python réel pourrait être plus compréhensible.
John Machin