343 votes

Rechercher et remplacer une ligne dans un fichier en Python

Je veux boucler sur le contenu d'un fichier texte, effectuer une recherche et un remplacement sur certaines lignes et réécrire le résultat dans le fichier. Je pourrais d'abord charger tout le fichier en mémoire, puis le réécrire, mais ce n'est probablement pas la meilleure façon de procéder.

Quelle est la meilleure façon de procéder, dans le code suivant ?

f = open(file)
for line in f:
    if line.contains('foo'):
        newline = line.replace('foo', 'bar')
        # how to write this newline back to the file

293voto

Eli Bendersky Points 82298

Le moyen le plus court serait probablement d'utiliser la fonction module d'entrée de fichiers . Par exemple, l'exemple suivant ajoute des numéros de ligne à un fichier, en place :

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print "%d: %s" % (fileinput.filelineno(), line),

Ce qui se passe ici est :

  1. Le fichier original est déplacé vers un fichier de sauvegarde
  2. La sortie standard est redirigée vers le fichier original à l'intérieur de la boucle.
  3. Ainsi, tout print les instructions réécrivent dans le fichier original

fileinput a plus de cloches et de sifflets. Par exemple, il peut être utilisé pour opérer automatiquement sur tous les fichiers de la section sys.args[1:] sans que vous ayez à les itérer explicitement. À partir de Python 3.2, il fournit également un gestionnaire de contexte pratique à utiliser dans un environnement with déclaration.


Alors que fileinput est idéal pour les scripts à jeter, mais je me méfierais de son utilisation dans du code réel, car il est vrai qu'il n'est pas très lisible ou familier. Dans du code réel (de production), il vaut la peine de passer quelques lignes de code supplémentaires pour rendre le processus explicite et donc rendre le code lisible.

Il existe deux options :

  1. Le fichier n'est pas trop volumineux, et vous pouvez le lire entièrement en mémoire. Fermez ensuite le fichier, rouvrez-le en mode écriture et réécrivez le contenu modifié.
  2. Le fichier est trop volumineux pour être stocké en mémoire ; vous pouvez le déplacer vers un fichier temporaire et l'ouvrir, le lire ligne par ligne et le réécrire dans le fichier d'origine. Notez que cela nécessite deux fois plus de stockage.

17 votes

Je sais que cela ne comporte que deux lignes, mais je ne pense pas que le code soit très expressif en soi. Parce que si vous réfléchissez une seconde, si vous ne connaissez pas la fonction, il y a très peu d'indices de ce qui se passe. Imprimer le numéro de ligne et la ligne n'est pas la même chose que de l'écrire... si vous voyez ce que je veux dire...

3 votes

je suis d'accord. comment utiliser fileinput pour écrire dans le fichier ?

14 votes

Ce site FAIT écrire dans le fichier. Il redirige stdout vers le fichier. Jetez un coup d'œil à la docs

221voto

Thomas Watnedal Points 2405

Je suppose que quelque chose comme ça devrait le faire. En gros, il écrit le contenu dans un nouveau fichier et remplace l'ancien fichier par le nouveau :

from tempfile import mkstemp
from shutil import move
from os import remove, close

def replace(file_path, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    new_file = open(abs_path,'w')
    old_file = open(file_path)
    for line in old_file:
        new_file.write(line.replace(pattern, subst))
    #close temp file
    new_file.close()
    close(fh)
    old_file.close()
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)

7 votes

Juste un petit commentaire : file est l'ombre de la classe prédéfinie du même nom.

0 votes

@ezdazuzena C'est un bon point. J'ai remplacé file par file_path

4 votes

Ce code change les permissions sur le fichier original. Comment puis-je conserver les permissions d'origine ?

89voto

Jason Points 1839

Voici un autre exemple qui a été testé et qui correspond aux modèles de recherche et de remplacement :

import fileinput
import sys

def replaceAll(file,searchExp,replaceExp):
    for line in fileinput.input(file, inplace=1):
        if searchExp in line:
            line = line.replace(searchExp,replaceExp)
        sys.stdout.write(line)

Exemple d'utilisation :

replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")

26 votes

L'exemple d'utilisation fournit une expression régulière, mais aucune searchExp in line ni line.replace sont des opérations d'expression régulière. L'exemple utilisé est certainement erroné.

0 votes

Au lieu de if searchExp in line: line = line.replace(searchExp, replaceExpr) vous pouvez simplement écrire line = line.replace(searchExp, replaceExpr) . Aucune exception n'est générée, la ligne reste simplement inchangée.

0 votes

Cela a parfaitement fonctionné pour moi aussi. J'avais rencontré un certain nombre d'autres exemples qui ressemblaient beaucoup à celui-ci, mais l'astuce était l'utilisation de l'attribut sys.stdout.write(line) . Merci encore !

68voto

Kinlan Points 7858

Cela devrait fonctionner : (édition sur place)

import fileinput

for line in fileinput.input(files, inplace = 1): # Does a list of files, and writes redirects STDOUT to the file in question
      print line.replace("foo", "bar"),

5 votes

+1. En outre, si vous recevez un RuntimeError : input() déjà actif, appelez le fichierinput.close().

3 votes

Notez que files doit être une chaîne de caractères contenant le nom du fichier, pas un objet fichier .

10 votes

print ajoute une nouvelle ligne qui pourrait déjà être là. Pour éviter cela, ajoutez .rstrip() à la fin de vos remplacements.

24voto

Thijs Points 51

Basé sur la réponse de Thomas Watnedal. Cependant, cela ne répond pas exactement à la partie ligne à ligne de la question initiale. La fonction peut toujours remplacer, ligne par ligne, les éléments suivants

Cette mise en œuvre remplace le contenu du fichier sans utiliser de fichiers temporaires, par conséquent, les autorisations de fichiers restent inchangées.

De même, re.sub au lieu de replace, permet le remplacement par regex au lieu du remplacement par texte brut uniquement.

La lecture du fichier en tant que chaîne unique plutôt que ligne par ligne permet une correspondance et un remplacement multilignes.

import re

def replace(file, pattern, subst):
    # Read contents from file as a single string
    file_handle = open(file, 'r')
    file_string = file_handle.read()
    file_handle.close()

    # Use RE package to allow for replacement (also allowing for (multiline) REGEX)
    file_string = (re.sub(pattern, subst, file_string))

    # Write contents to file.
    # Using mode 'w' truncates the file.
    file_handle = open(file, 'w')
    file_handle.write(file_string)
    file_handle.close()

2 votes

Vous pouvez utiliser rb et wb lors de l'ouverture des fichiers, car cela permet de préserver les fins de ligne originales.

0 votes

Dans Python 3, vous ne pouvez pas utiliser 'wb' et 'rb' avec 're'. Cela donnera l'erreur "TypeError : cannot use a string pattern on a bytes-like object".

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