Question

Je veux faire une boucle sur le contenu d'un fichier texte et de faire une recherche et remplacer sur certaines lignes et écrire le résultat dans le fichier.Je pourrais d'abord charger tout le fichier en mémoire, puis l'écrire, mais qui n'est probablement pas la meilleure façon de le faire.

Quelle est la meilleure façon pour ce faire, 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
Était-ce utile?

La solution

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

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

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

Autres conseils

Le chemin le plus court serait probablement d'utiliser le fileinput module.Ainsi, l'exemple suivant ajoute des numéros de ligne dans un fichier, sur 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 d'origine est déplacé vers un fichier de sauvegarde
  2. La sortie standard est redirigée vers le fichier d'origine à l'intérieur de la boucle
  3. Ainsi, tout print les déclarations d'écrire dans le fichier d'origine

fileinput a plus de cloches et de sifflets.Par exemple, il peut être utilisé pour commander automatiquement sur tous les fichiers dans sys.args[1:], sans avoir à parcourir de façon explicite.Commencer avec Python 3.2 il fournit également une pratique gestionnaire de contexte pour une utilisation dans un with l'énoncé.


Alors que fileinput est idéal pour jeter de scripts, je me méfie de l'utiliser dans le code réel, car il est vrai qu'il n'est pas très lisible ou familier.Dans la vraie (la production) du code, il est intéressant de passer un peu plus de lignes de code pour rendre le processus explicite et donc de rendre le code lisible.

Il y a deux options:

  1. Le fichier n'est pas trop grande, et il vous suffit de le lire entièrement à la mémoire.Ensuite, fermez le fichier et l'ouvrir à nouveau en mode d'écriture, et écrire le contenu modifié en arrière.
  2. Le fichier est trop volumineux pour être stockées dans la mémoire;vous pouvez vous déplacer dans un fichier temporaire et ouvert que, la lecture ligne par ligne, l'écriture dans le fichier d'origine.À noter que cela nécessite deux fois plus de stockage.

Voici un autre exemple qui a été testé, et correspondent à rechercher & remplacer des modèles:

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

Cela devrait fonctionner:(place de l'édition)

import fileinput

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

Basé sur la réponse de Thomas Watnedal.Cependant, cela ne répond pas à la ligne-à-ligne une partie de la question d'origine exactement.La fonction peut toujours remplacer sur une ligne-à-ligne de base

Cette application remplace le contenu du fichier sans l'aide de fichiers temporaires, comme une conséquence les autorisations de fichier reste inchangé.

Aussi ré.sous au lieu de remplacer, de regex permet de remplacement au lieu de la plaine de remplacement de texte seulement.

De la lecture du fichier comme une seule chaîne à la place de, ligne par ligne, permet multiligne match et de remplacement.

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

Comme lassevk suggère, d'écrire le nouveau fichier que vous allez, voici un exemple de code:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()

Si vous êtes désireux d'une fonction générique qui remplace tout texte avec un autre texte, c'est probablement la meilleure façon d'y aller, surtout si vous êtes un fan de la regex est:

import re
def replace( filePath, text, subs, flags=0 ):
    with open( filePath, "r+" ) as file:
        fileContents = file.read()
        textPattern = re.compile( re.escape( text ), flags )
        fileContents = textPattern.sub( subs, fileContents )
        file.seek( 0 )
        file.truncate()
        file.write( fileContents )

Plus pythonic façon serait d'utiliser le contexte des gestionnaires comme le code ci-dessous:

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

def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()
    with open(target_file_path, 'w') as target_file:
        with open(source_file_path, 'r') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

Vous trouverez l'intégralité extrait de ici.

Créer un nouveau fichier, copier les lignes de l'ancien vers le nouveau, et de faire les remplacer avant de vous écrire les lignes vers le nouveau fichier.

L'expansion sur @Kiran réponse, qui j'en conviens est plus succinct et Pythonic, ce qui ajoute des codecs pour la lecture et l'écriture de l'UTF-8:

import codecs 

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


def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()

    with codecs.open(target_file_path, 'w', 'utf-8') as target_file:
        with codecs.open(source_file_path, 'r', 'utf-8') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

À l'aide de hamishmcn de répondre en tant que modèle, j'ai été capable de rechercher une ligne dans un fichier qui correspond à mes regex et de la remplacer par une chaîne vide.

import re 

fin = open("in.txt", 'r') # in file
fout = open("out.txt", 'w') # out file
for line in fin:
    p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern
    newline = p.sub('',line) # replace matching strings with empty string
    print newline
    fout.write(newline)
fin.close()
fout.close()

si vous supprimer le tiret à la comme ci-dessous, afin de rechercher et remplacer dans plusieurs ligne.Voir ci-dessous par exemple.

def replace(file, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    print fh, abs_path
    new_file = open(abs_path,'w')
    old_file = open(file)
    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)
    #Move new file
    move(abs_path, file)

Pour les utilisateurs de Linux:

import os
os.system('sed -i \'s/foo/bar/\' '+file_path)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top