Come cercare e sostituire il testo in un file?


212

Come posso cercare e sostituire il testo in un file usando Python 3?

Ecco il mio codice:

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

File di input:

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

Quando cerco e sostituisco 'ram' con 'abcd' nel file di input sopra, funziona come un incantesimo. Ma quando lo faccio viceversa, ovvero sostituendo "abcd" con "ram", alcuni personaggi spazzatura vengono lasciati alla fine.

Sostituzione di 'abcd' con 'ram'

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

Puoi essere un po 'più specifico quando dici "alcuni personaggi spazzatura sono rimasti alla fine", cosa vedi?
Burhan Khalid,

Aggiornata la domanda con l'output di ciò che ho ottenuto
Shriram,

Risposte:


241

fileinputsupporta già la modifica sul posto. In stdoutquesto caso reindirizza al file:

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

13
Cosa end=''dovrebbe fare l' argomento?
egpbos,

18
lineha già una nuova riga. endè una nuova riga per impostazione predefinita, end=''rende la print()funzione non stampare una nuova riga aggiuntiva
jfs

11
Non usare fileinput! Valuta invece di scrivere il codice per farlo da solo. Il reindirizzamento di sys.stdout non è una grande idea, soprattutto se lo stai facendo senza un tentativo..finalmente come fa fileinput. Se viene sollevata un'eccezione, il tuo stdout potrebbe non essere mai ripristinato.
Craigds

9
@craigds: sbagliato. fileinputnon è uno strumento per tutti i lavori ( nulla lo è) ma ci sono molti casi in cui è lo strumento giusto, ad esempio, per implementare un sedfiltro simile a Python. Non usare un cacciavite per battere le unghie.
jfs

5
Se vuoi davvero reindirizzare stdout al tuo file per qualche motivo, non è difficile farlo meglio di fileinputcosì (in pratica, usa try..finallyo un gestore di contesto per assicurarti di riportare stdout al suo valore originale in seguito). Il codice sorgente di fileinputè terribilmente terrificante e fa cose davvero pericolose sotto il cofano. Se fosse stato scritto oggi dubito fortemente che sarebbe arrivato allo stdlib.
Craigds,

333

Come sottolineato da michaelb958, non è possibile sostituirlo con dati di lunghezza diversa perché ciò metterà fuori posto il resto delle sezioni. Non sono d'accordo con gli altri poster che suggeriscono di leggere da un file e scrivere ad un altro. Invece, vorrei leggere il file in memoria, correggere i dati e quindi scriverlo nello stesso file in un passaggio separato.

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

A meno che tu non abbia un enorme file con cui lavorare che è troppo grande per essere caricato in memoria in una sola volta, o non sei preoccupato per la potenziale perdita di dati se il processo viene interrotto durante il secondo passaggio in cui scrivi i dati nel file.


5
with file = open(..):Python ( =) non è valido sebbene l'intento sia chiaro. .replace()non modifica la stringa (è immutabile) quindi è necessario utilizzare il valore restituito. Ad ogni modo il codice che supporta file di grandi dimensioni può essere ancora più semplice a meno che non sia necessario cercare e sostituire il testo che si estende su più righe.
jfs,

40
Hai ragione, e questo - gente - è per questo che dovresti testare il tuo codice prima di metterti in imbarazzo su Internet;)
Jack Aidley,

19
@JonasStein: No, non dovrebbe. L' withistruzione chiude automaticamente il file alla fine del blocco di istruzioni.
Jack Aidley,

2
@JackAidley è interessante. Grazie per la spiegazione.
Jonas Stein,

4
@JackAidley perché è breve, semplice, facilmente utilizzabile e comprensibile e affronta un problema reale che molte persone hanno (e quindi che molte persone cercano, trovando così la tua risposta).
Ben Barden,

52

Come Jack Aidley aveva pubblicato e sottolineato da JF Sebastian, questo codice non funzionerà:

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

Ma questo codice funzionerà (l'ho provato):

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

Usando questo metodo, filein e fileout possono essere lo stesso file, perché Python 3.3 sovrascriverà il file all'apertura per la scrittura.


9
Credo che la differenza sia qui: filedata.replace ('ram', 'abcd') Rispetto a: newdata = filedata.replace ("vecchi dati", "nuovi dati") Niente a che fare con l'affermazione "con"
Diegomanas

5
1. perché dovresti rimuovere with-statement? 2. Come indicato nella mia risposta, fileinputpuò funzionare direttamente: può sostituire i dati nello stesso file (utilizza internamente un file temporaneo). La differenza è che fileinputnon è necessario caricare l'intero file in memoria.
jfs,

8
Solo per salvare gli altri rivisitando la risposta di Jack Aidley, è stata corretta da questa risposta, quindi questa è ora ridondante (e inferiore a causa della perdita dei withblocchi più ordinati ).
Chris,

46

Puoi fare la sostituzione in questo modo

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

7

Puoi anche usare 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)

Grazie Yuya. La soluzione sopra ha funzionato bene. Nota: è necessario prima eseguire il backup del file originale, poiché sostituisce il file originale stesso. Se si desidera sostituire ripetutamente il testo, è possibile continuare ad aggiungere le ultime 2 righe come di seguito. text = text.replace (text_to_search, sostituzione_testo) path.write_text (testo)
Nages

3

Con un singolo con blocco, puoi cercare e sostituire il testo:

with open('file.txt','r+') as f:
    filedata = f.read()
    filedata = filedata.replace('abc','xyz')
    f.truncate(0)
    f.write(filedata)

1
Hai dimenticato di seekiniziare il file prima di scriverlo. truncatenon lo fa e quindi avrai immondizia nel file.
ur.

2

Il tuo problema deriva dalla lettura e dalla scrittura nello stesso file. Invece di aprirefileToSearch per la scrittura, apri un file temporaneo effettivo e dopo aver finito e chiuso tempFile, usa os.renameper spostare il nuovo file fileToSearch.


1
Cordiali saluti (sentiti libero di modificare la risposta): la causa principale non è quella di accorciare la metà di un file in atto. Cioè, se cerchi 5 caratteri e li sostituisci con 3, verranno sostituiti i primi 3 caratteri dei 5 cercati; ma gli altri 2 non possono essere rimossi, rimarranno lì. La soluzione di file temporaneo rimuove questi caratteri "rimanenti" rilasciandoli invece di scriverli nel file temporaneo.
michaelb958 - GoFundMonica,

2

(installa pip python-util)

from pyutil import filereplace

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

Il secondo parametro (l'elemento da sostituire, ad esempio "abcd" può anche essere una regex)
sostituirà tutte le occorrenze


Ho avuto una brutta esperienza con questo (ha aggiunto alcuni caratteri alla fine del file), quindi non posso raccomandarlo, anche se un one-liner sarebbe carino.
Azrael3000,

@ Azrael3000 Ha aggiunto personaggi? Non ho visto che mi succedesse. Ti sarei molto grato se avessi aperto un problema su Github in modo da poterlo risolvere github.com/MisterL2/python-util
MisterL2

1

La mia variante, una parola alla volta sull'intero file.

L'ho letto in memoria.

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)

0

Ho fatto questo:

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

Triste, ma fileinput non funziona inplace=Truecon utf-8.
Sergio,

0

Ho modificato leggermente il post di Jayram Singh per sostituire ogni istanza di un "!" carattere a un numero che volevo incrementare con ogni istanza. Ho pensato che potesse essere utile a qualcuno che voleva modificare un personaggio che si verificava più di una volta per riga e voleva iterare. Spero che aiuti qualcuno. PS: sono molto nuovo nella programmazione, quindi mi scuso se il mio post non è appropriato in alcun modo, ma questo ha funzionato per me.

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

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

Questo codice sostituirà la parola che intendi. l'unico problema è che riscrive l'intero file. potrebbe bloccarsi se il file è troppo lungo per essere gestito dal processore.
Vinit Pillai,

0

Così:

def find_and_replace(file, word, replacement):
  with open(file, 'r+') as f:
    text = f.read()
    f.write(text.replace(word, replacement))

Assicurati che la tua risposta migliori su altre risposte già presenti in questa domanda.
hongsy,

Questo aggiungerà il testo con la sostituzione alla fine del file, secondo me @Jack Aidley aswer è esattamente ciò che OP significava 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) 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.