¿Cómo buscar y reemplazar texto en un archivo?

212

¿Cómo busco y reemplazo texto en un archivo usando Python 3?

Aquí está mi código:

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

Fichero de entrada:

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

Cuando busco y reemplazo 'ram' por 'abcd' en el archivo de entrada anterior, funciona como un encanto. Pero cuando lo hago al revés, es decir, reemplazando 'abcd' por 'ram', quedan algunos caracteres basura al final.

Reemplazando 'abcd' por 'ram'

hi this is ram hi this is ram
This is dummy text file.
This is how search and replace works rambcd
Shriram
fuente
¿Puedes ser un poco más específico cuando dices "al final quedan algunos caracteres basura", qué ves?
Burhan Khalid
Actualicé la pregunta con salida de lo que obtuve.
Shriram

Respuestas:

241

fileinputya es compatible con la edición in situ. Redirige stdoutal archivo en este caso:

#!/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
fuente
13
¿Qué se end=''supone que debe hacer el argumento?
egpbos 01 de
18
lineYa tiene una nueva línea. endes una nueva línea por defecto, end=''hace que la print()función no imprima nueva línea adicional
jfs
11
¡No utilices fileinput! Considere escribir el código para hacerlo usted mismo. Redirigir sys.stdout no es una gran idea, especialmente si lo estás haciendo sin probar ... finalmente como lo hace fileinput. Si se genera una excepción, es posible que su stdout nunca se restablezca.
craigds
9
@craigds: mal. fileinputno es una herramienta para todos los trabajos ( nada lo es) pero hay muchos casos en los que es la herramienta adecuada, por ejemplo, para implementar un sedfiltro similar en Python. No use un destornillador para golpear las uñas.
jfs
55
Si realmente desea redirigir stdout a su archivo por alguna razón, no es difícil hacerlo mejor de fileinputlo que lo hace (básicamente, use try..finallyo un administrador de contexto para asegurarse de volver a establecer stdout en su valor original después). El código fuente fileinputes bastante horrible, y hace algunas cosas realmente inseguras bajo el capó. Si se hubiera escrito hoy, dudo mucho que hubiera llegado al stdlib.
craigds
333

Como lo señaló michaelb958, no puede reemplazar en el lugar con datos de una longitud diferente porque esto hará que el resto de las secciones estén fuera de lugar. No estoy de acuerdo con los otros carteles que sugieren que lea de un archivo y escriba en otro. En cambio, leería el archivo en la memoria, arreglaría los datos y luego lo escribiría en el mismo archivo en un paso separado.

# 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 menos que tenga un archivo masivo para trabajar que sea demasiado grande para cargar en la memoria de una sola vez, o le preocupe la posible pérdida de datos si el proceso se interrumpe durante el segundo paso en el que escribe datos en el archivo.

Jack Aidley
fuente
55
with file = open(..):no es válido Python ( =) aunque la intención es clara. .replace()no modifica la cadena (es inmutable), por lo que debe usar el valor devuelto. De todos modos, el código que admite archivos grandes puede ser aún más simple a menos que necesite buscar y reemplazar texto que abarque varias líneas.
jfs
40
Tienes toda la razón, y esa es la razón por la que debes probar tu código antes de avergonzarte en Internet;)
Jack Aidley
19
@JonasStein: No, no debería. La withdeclaración cierra automáticamente el archivo al final del bloque de la declaración.
Jack Aidley
2
@JackAidley que es interesante. Gracias por la explicación.
Jonas Stein
44
@JackAidley porque es corto, simple, fácil de usar y comprender, y aborda un problema real que mucha gente tiene (y por lo tanto mucha gente busca, encontrando así su respuesta).
Ben Barden
52

Como Jack Aidley había publicado y JF Sebastian señaló, este código no funcionará:

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

Pero este código funcionará (lo he probado):

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

Con este método, filein y fileout pueden ser el mismo archivo, porque Python 3.3 sobrescribirá el archivo al abrirlo para escribir.

Neamerjell
fuente
9
Creo que la diferencia está aquí: filedata.replace ('ram', 'abcd') Comparado con: newdata = filedata.replace ("datos antiguos", "datos nuevos") Nada que ver con la declaración "con"
Diegomanas
55
1. ¿por qué eliminarías with-declaración? 2. Como se indicó en mi respuesta, fileinputpuede funcionar en el lugar, puede reemplazar los datos en el mismo archivo (utiliza un archivo temporal internamente). La diferencia es que fileinputno requiere cargar todo el archivo en la memoria.
jfs
8
Solo para salvar a otros que vuelven a visitar la respuesta de Jack Aidley, se ha corregido desde esta respuesta, por lo que esta ahora es redundante (e inferior debido a la pérdida de los withbloques más limpios ).
Chris
46

Puedes hacer el reemplazo así

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
fuente
7

También puedes usar 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
fuente
Gracias Yuya La solución anterior funcionó bien. Nota: Primero debe hacer una copia de seguridad de su archivo original, ya que reemplaza su archivo original. Si desea reemplazar texto repetidamente, puede seguir agregando las últimas 2 líneas como se muestra a continuación. text = text.replace (text_to_search, replace_text) path.write_text (text)
Nages
3

Con un solo bloque, puede buscar y reemplazar su texto:

with open('file.txt','r+') as f:
    filedata = f.read()
    filedata = filedata.replace('abc','xyz')
    f.truncate(0)
    f.write(filedata)
iknowitwasyoufredo
fuente
1
Olvidaste al seekprincipio del archivo antes de escribirlo. truncateno hace eso y tendrás basura en el archivo.
ur.
2

Su problema proviene de leer y escribir en el mismo archivo. En lugar de abrir fileToSearchpara escribir, abra un archivo temporal real y luego, una vez que haya terminado y haya cerrado tempFile, use os.renamepara mover el nuevo archivo fileToSearch.

icktoofay
fuente
1
FYI amigable (siéntase libre de editar en la respuesta): La causa raíz no es poder acortar la mitad de un archivo en su lugar. Es decir, si busca 5 caracteres y los reemplaza por 3, se reemplazarán los primeros 3 caracteres de los 5 buscados; pero los otros 2 no se pueden eliminar, simplemente se quedarán allí. La solución de archivo temporal elimina estos caracteres "sobrantes" colocándolos en lugar de escribirlos en el archivo temporal.
michaelb958 - GoFundMonica
2

(pip install python-util)

from pyutil import filereplace

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

El segundo parámetro (la cosa a ser reemplazada, por ejemplo, "abcd" también puede ser una expresión regular)
Reemplazará todas las ocurrencias

MisterL2
fuente
Tuve una mala experiencia con esto (agregó algunos caracteres al final del archivo), por lo que no puedo recomendarlo, aunque una frase sería buena.
Azrael3000
@ Azrael3000 ¿Agregó caracteres? No he visto que me pase a mí. Le agradecería mucho que abriera un problema en Github para poder solucionarlo github.com/MisterL2/python-util
MisterL2
1

Mi variante, una palabra a la vez en todo el archivo.

Lo leí en la 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)
LiPi
fuente
0

He hecho esto:

#!/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
fuente
Triste, pero fileinput no trabajas inplace=Truecon utf-8.
Sergio
0

Modifiqué un poco la publicación de Jayram Singh para reemplazar cada instancia de un '!' carácter a un número que quería incrementar con cada instancia. Pensé que podría ser útil para alguien que quería modificar un carácter que ocurría más de una vez por línea y quería iterar. Espero que ayude a alguien. PD: soy muy nuevo en la codificación, así que me disculpo si mi publicación es inapropiada de alguna manera, pero esto funcionó para mí.

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
fuente
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
fuente
Este código reemplazará la palabra que desea. El único problema es que reescribe todo el archivo. podría atascarse si el archivo es demasiado largo para que el procesador lo maneje.
Vinit Pillai el
0

Al igual que:

def find_and_replace(file, word, replacement):
  with open(file, 'r+') as f:
    text = f.read()
    f.write(text.replace(word, replacement))
Cyril Alohan
fuente
Asegúrese de que su respuesta mejore sobre otras respuestas ya presentes en esta pregunta.
hongsy
Esto agregará el texto con reemplazo al final del archivo, en mi opinión, @Jack Aidley aswer es justo lo que OP significó 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
fuente