Comment rechercher et remplacer du texte dans un fichier?

212

Comment rechercher et remplacer du texte dans un fichier à l'aide de Python 3?

Voici mon code:

import os
import sys
import fileinput

print ("Text to search for:")
textToSearch = input( "> " )

print ("Text to replace it with:")
textToReplace = input( "> " )

print ("File to perform Search-Replace on:")
fileToSearch  = input( "> " )
#fileToSearch = 'D:\dummy1.txt'

tempFile = open( fileToSearch, 'r+' )

for line in fileinput.input( fileToSearch ):
    if textToSearch in line :
        print('Match Found')
    else:
        print('Match Not Found!!')
    tempFile.write( line.replace( textToSearch, textToReplace ) )
tempFile.close()


input( '\n\n Press Enter to exit...' )

Fichier d'entrée:

hi this is abcd hi this is abcd
This is dummy text file.
This is how search and replace works abcd

Lorsque je recherche et remplace «ram» par «abcd» dans le fichier d'entrée ci-dessus, cela fonctionne comme un charme. Mais quand je le fais vice-versa, c'est-à-dire en remplaçant «abcd» par «ram», certains caractères indésirables sont laissés à la fin.

Remplacement de 'abcd' par 'ram'

hi this is ram hi this is ram
This is dummy text file.
This is how search and replace works rambcd
Shriram
la source
Pouvez-vous être un peu plus précis lorsque vous dites "certains caractères indésirables restent à la fin", que voyez-vous?
Burhan Khalid
Mise à jour de la question avec sortie ce que j'ai obtenu.
Shriram

Réponses:

241

fileinputprend déjà en charge l'édition sur place. Il redirige stdoutvers le fichier dans ce cas:

#!/usr/bin/env python3
import fileinput

with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
    for line in file:
        print(line.replace(text_to_search, replacement_text), end='')
jfs
la source
13
Quel est l' end=''argument censé faire?
egpbos
18
linea déjà une nouvelle ligne. endest une nouvelle ligne par défaut, end=''fait que la print()fonction n'imprime pas de nouvelle ligne supplémentaire
jfs
11
N'utilisez pas fileinput! Pensez à écrire le code pour le faire vous-même à la place. Rediriger sys.stdout n'est pas une bonne idée, surtout si vous le faites sans essayer ... comme le fait fileinput. Si une exception est levée, votre sortie standard risque de ne jamais être restaurée.
craigds
9
@craigds: faux. fileinputn'est pas un outil pour tous les emplois ( rien est) , mais il y a beaucoup de cas où il est l'outil approprié , par exemple, mettre en place un sedfiltre -comme en Python. N'utilisez pas de tournevis pour marteler les ongles.
jfs
5
Si vous voulez vraiment rediriger stdout vers votre fichier pour une raison quelconque, il n'est pas difficile de le faire mieux que le fileinputfait (en gros, utilisez try..finallyou un gestionnaire de contexte pour vous assurer de remettre stdout à sa valeur d'origine par la suite). Le code source de fileinputest assez affreux, et il fait des choses vraiment dangereuses sous le capot. Si elle avait été écrite aujourd'hui, je doute fort qu'elle aurait pu figurer dans le stdlib.
craigds
333

Comme l'a souligné michaelb958, vous ne pouvez pas remplacer en place par des données d'une longueur différente car cela mettra le reste des sections à leur place. Je ne suis pas d'accord avec les autres affiches vous suggérant de lire un fichier et d'écrire dans un autre. Au lieu de cela, je lis le fichier en mémoire, corrige les données, puis l'écris dans le même fichier dans une étape distincte.

# Read in the file
with open('file.txt', 'r') as file :
  filedata = file.read()

# Replace the target string
filedata = filedata.replace('ram', 'abcd')

# Write the file out again
with open('file.txt', 'w') as file:
  file.write(filedata)

À moins que vous n'ayez un fichier volumineux à travailler qui soit trop volumineux pour être chargé en mémoire en une seule fois, ou que vous ne soyez préoccupé par la perte potentielle de données si le processus est interrompu pendant la deuxième étape au cours de laquelle vous écrivez des données dans le fichier.

Jack Aidley
la source
5
with file = open(..):n'est pas valide Python ( =) bien que l'intention soit claire. .replace()ne modifie pas la chaîne (elle est immuable), vous devez donc utiliser la valeur renvoyée. Quoi qu'il en soit, le code qui prend en charge les gros fichiers peut être encore plus simple, sauf si vous devez rechercher et remplacer du texte qui s'étend sur plusieurs lignes.
jfs
40
Vous avez tout à fait raison, et c'est - les gens - c'est pourquoi vous devriez tester votre code avant de vous embarrasser sur Internet;)
Jack Aidley
19
@ JonasStein: Non, ça ne devrait pas. L' withinstruction ferme automatiquement le fichier à la fin du bloc d'instruction.
Jack Aidley
2
@JackAidley c'est intéressant. Merci pour l'explication.
Jonas Stein
4
@JackAidley parce qu'il est court, simple, facile à utiliser et à comprendre, et répond à un vrai problème que beaucoup de gens ont (et donc que beaucoup de gens recherchent - trouvant ainsi votre réponse).
Ben Barden
52

Comme Jack Aidley l'avait signalé et JF Sebastian l'a souligné, ce code ne fonctionnera pas:

 # Read in the file
filedata = None
with file = open('file.txt', 'r') :
  filedata = file.read()

# Replace the target string
filedata.replace('ram', 'abcd')

# Write the file out again
with file = open('file.txt', 'w') :
  file.write(filedata)`

Mais ce code fonctionnera (je l'ai testé):

f = open(filein,'r')
filedata = f.read()
f.close()

newdata = filedata.replace("old data","new data")

f = open(fileout,'w')
f.write(newdata)
f.close()

En utilisant cette méthode, filein et fileout peuvent être le même fichier, car Python 3.3 écrasera le fichier lors de son ouverture en écriture.

Neamerjell
la source
9
Je crois que la différence est ici: filedata.replace ( « ram », « abcd ») Par rapport à: newdata = filedata.replace ( « anciennes données », « nouvelles données ») Rien à voir avec le « par » déclaration
Diegomanas
5
1. pourquoi voudriez-vous supprimer la withdéclaration? 2. Comme indiqué dans ma réponse, fileinputpeut fonctionner sur place - il peut remplacer les données dans le même fichier (il utilise un fichier temporaire en interne). La différence est qu'il fileinputne nécessite pas de charger tout le fichier en mémoire.
jfs
8
Juste pour sauver les autres revisitant la réponse de Jack Aidley, elle a été corrigée depuis cette réponse, donc celle-ci est maintenant redondante (et inférieure en raison de la perte des withblocs plus nets ).
Chris
46

Vous pouvez faire le remplacement comme ceci

f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
for line in f1:
    f2.write(line.replace('old_text', 'new_text'))
f1.close()
f2.close()
Jayram
la source
7

Vous pouvez également utiliser pathlib.

from pathlib2 import Path
path = Path(file_to_search)
text = path.read_text()
text = text.replace(text_to_search, replacement_text)
path.write_text(text)
Yuya Takashina
la source
Merci Yuya. La solution ci-dessus a bien fonctionné. Remarque: vous devez d'abord effectuer une sauvegarde de votre fichier d'origine, car il remplace votre fichier d'origine lui-même. Si vous souhaitez remplacer à plusieurs reprises le texte, vous pouvez continuer à ajouter les 2 dernières lignes comme ci-dessous. text = text.replace (text_to_search, remplacement_text) path.write_text (text)
Nages
3

Avec un simple avec bloc, vous pouvez rechercher et remplacer votre texte:

with open('file.txt','r+') as f:
    filedata = f.read()
    filedata = filedata.replace('abc','xyz')
    f.truncate(0)
    f.write(filedata)
iknowitwasyoufredo
la source
1
Vous avez oublié au seekdébut du fichier avant de l'écrire. truncatene fait pas cela et vous aurez donc des ordures dans le fichier.
ur.
2

Votre problème provient de la lecture et de l'écriture dans le même fichier. Plutôt que d'ouvrir fileToSearchpour l'écriture, ouvrez un fichier temporaire réel, puis une fois que vous avez terminé et fermé tempFile, utilisez os.renamepour déplacer le nouveau fichier fileToSearch.

icktoofay
la source
1
FYI amical (n'hésitez pas à éditer dans la réponse): La cause première n'est pas en mesure de raccourcir le milieu d'un fichier en place. Autrement dit, si vous recherchez 5 caractères et remplacez par 3, les 3 premiers caractères des 5 recherchés seront remplacés; mais les 2 autres ne peuvent pas être supprimés, ils y resteront. La solution de fichier temporaire supprime ces caractères "restants" en les supprimant au lieu de les écrire dans le fichier temporaire.
michaelb958 - GoFundMonica
2

(pip installe python-util)

from pyutil import filereplace

filereplace("somefile.txt","abcd","ram")

Le deuxième paramètre (la chose à remplacer, par exemple "abcd" peut également être une expression régulière)
remplacera toutes les occurrences

MisterL2
la source
J'ai eu une mauvaise expérience avec cela (il a ajouté quelques caractères à la fin du fichier), donc je ne peux pas le recommander, même si un seul revêtement serait bien.
Azrael3000
@ Azrael3000 Il a ajouté des personnages? Je n'ai pas vu cela m'arriver. J'apprécierais grandement si vous ouvriez
MisterL2
1

Ma variante, un mot à la fois sur l'ensemble du dossier.

Je l'ai lu en mémoire.

def replace_word(infile,old_word,new_word):
    if not os.path.isfile(infile):
        print ("Error on replace_word, not a regular file: "+infile)
        sys.exit(1)

    f1=open(infile,'r').read()
    f2=open(infile,'w')
    m=f1.replace(old_word,new_word)
    f2.write(m)
LiPi
la source
0

Je l'ai fait:

#!/usr/bin/env python3

import fileinput
import os

Dir = input ("Source directory: ")
os.chdir(Dir)

Filelist = os.listdir()
print('File list: ',Filelist)

NomeFile = input ("Insert file name: ")

CarOr = input ("Text to search: ")

CarNew = input ("New text: ")

with fileinput.FileInput(NomeFile, inplace=True, backup='.bak') as file:
    for line in file:
        print(line.replace(CarOr, CarNew), end='')

file.close ()
Zelmik
la source
Triste, mais fileinput ne fonctionne pas inplace=Trueavec utf-8.
Sergio
0

J'ai légèrement modifié le post de Jayram Singh afin de remplacer chaque instance d'un '!' caractère à un nombre que je voulais incrémenter à chaque instance. Je pensais que cela pourrait être utile à quelqu'un qui voulait modifier un personnage qui se produisait plus d'une fois par ligne et voulait itérer. J'espère que cela aide quelqu'un. PS- Je suis très nouveau dans le codage, donc je m'excuse si mon message est inapproprié de quelque façon que ce soit, mais cela a fonctionné pour moi.

f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
n = 1  

# if word=='!'replace w/ [n] & increment n; else append same word to     
# file2

for line in f1:
    for word in line:
        if word == '!':
            f2.write(word.replace('!', f'[{n}]'))
            n += 1
        else:
            f2.write(word)
f1.close()
f2.close()
Doc5506
la source
0
def word_replace(filename,old,new):
    c=0
    with open(filename,'r+',encoding ='utf-8') as f:
        a=f.read()
        b=a.split()
        for i in range(0,len(b)):
            if b[i]==old:
                c=c+1
        old=old.center(len(old)+2)
        new=new.center(len(new)+2)
        d=a.replace(old,new,c)
        f.truncate(0)
        f.seek(0)
        f.write(d)
    print('All words have been replaced!!!')
Vinit Pillai
la source
Ce code remplacera le mot que vous souhaitez. le seul problème est qu'il réécrit tout le fichier. peut rester bloqué si le fichier est trop long pour être traité par le processeur.
Vinit Pillai
0

Ainsi:

def find_and_replace(file, word, replacement):
  with open(file, 'r+') as f:
    text = f.read()
    f.write(text.replace(word, replacement))
Cyril Alohan
la source
Veuillez vous assurer que votre réponse améliore les autres réponses déjà présentes dans cette question.
Hongsy
Cela ajoutera le texte avec remplacement à la fin du fichier, à mon avis @Jack Aidley aswer est exactement ce que OP signifiait stackoverflow.com/a/17141572/6875391
Kirill
-3
def findReplace(find, replace):

    import os 

    src = os.path.join(os.getcwd(), os.pardir) 

    for path, dirs, files in os.walk(os.path.abspath(src)):

        for name in files: 

            if name.endswith('.py'): 

                filepath = os.path.join(path, name)

                with open(filepath) as f: 

                    s = f.read()

                s = s.replace(find, replace) 

                with open(filepath, "w") as f:

                    f.write(s) 
Deepak G
la source