183 votes

Comment supprimer une ligne spécifique dans un fichier ?

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

1 votes

Essayez fileinput comme décrit par @j-f-sebastian aquí . Il semble vous permettre de travailler ligne par ligne, via un fichier temporaire, le tout avec une simple for la syntaxe.

241voto

houbysoft Points 9773

Tout d'abord, ouvrez le fichier et récupérez toutes vos lignes dans le fichier. Ensuite, rouvrez le fichier en mode écriture et réécrivez vos lignes, à l'exception de celle que vous voulez 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 devez strip("\n") le caractère de fin de ligne dans la comparaison, car si votre fichier ne se termine pas par un caractère de fin de ligne, le tout dernier caractère de fin de ligne de votre fichier sera utilisé. line ne le fera pas non plus.

3 votes

Pourquoi devons-nous l'ouvrir et le fermer deux fois ?

5 votes

@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 accessible en écriture et vous remplacez le contenu du fichier sans la ligne que vous vouliez supprimer.

7 votes

Pourquoi Python ne nous permet-il pas de faire cela en une seule ligne ?

125voto

Lother Points 202

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 f-pointer puis truncate pour supprimer tout ce qui se trouve après la dernière écriture.

2 votes

Cela a très bien fonctionné pour moi, car j'ai dû utiliser lockfile également (fcntl). Je n'ai pas trouvé de moyen d'utiliser fileinput en même temps que fcntl.

1 votes

Il serait bon de voir quelques effets secondaires de cette solution.

6 votes

Je ne ferais pas ça. Si vous obtenez une erreur dans le for vous vous retrouverez avec un fichier partiellement écrasé, avec des lignes en double ou une ligne à moitié coupée. Vous pourriez vouloir f.truncate() juste après f.seek(0) à la place. De cette façon, si vous obtenez une erreur, vous vous retrouverez simplement avec un fichier incomplet. Mais la vraie solution (si vous avez l'espace disque nécessaire) est d'effectuer la sortie dans un fichier temporaire, puis d'utiliser la fonction os.replace() ou pathlib.Path(temp_filename).replace(original_filename) pour l'échanger avec l'original après que tout ait réussi.

45voto

Barnabe Points 340

La meilleure option et la plus rapide, 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 file_input:
    with open("newfile.txt", "w") as output: 
        for line in file_input:
            if line.strip("\n") != "nickname_to_delete":
                output.write(line)

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

0 votes

Au lieu d'utiliser une boucle for normale, nous pouvons utiliser la méthode suivante Expression du générateur De cette façon, le programme ne chargera pas toutes les lignes du fichier en 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 for ressemblera à ceci (output.write(line) for line in input if line!="nickname_to_delete"+"\n")

5 votes

@ShriShinde Vous ne lisez pas non plus le fichier en mémoire lorsque vous bouclez sur l'objet fichier, donc cette solution fonctionne de manière identique à votre suggestion.

0 votes

Vous pouvez supprimer le fichier original et renommer le second fichier au nom du fichier original, ce qui, avec Python sur un système d'exploitation Linux, ressemblerait à ceci, subprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])

33voto

ivanleoncz Points 1940

Il s'agit d'une "bifurcation" de @Lother (qui, à mon avis, 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 dérivation 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 rejettent l'utilisation de f.close()
  • plus clair if/else pour évaluer si la chaîne n'est pas présente dans la ligne courante

0 votes

Si f.seek(0) est nécessaire ?

0 votes

@yifan oui. Sinon, au lieu d'écraser le fichier, vous ajouterez le fichier à lui-même (sans les lignes que vous excluez).

7voto

Kingz Points 351

Le problème de la lecture des lignes au premier passage et des modifications (suppression de lignes spécifiques) au second passage est que si la taille de votre fichier est énorme, vous manquerez de RAM. Au lieu de cela, une meilleure approche est de lire les lignes, une par une, et de 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 du processeur indiquent que le traitement est en cours.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X