Jak wyszukiwać i zamieniać tekst w pliku?

212

Jak wyszukiwać i zamieniać tekst w pliku za pomocą Python 3?

Oto mój kod:

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

Plik wejściowy:

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

Kiedy szukam i zastępuję „ram” przez „abcd” w powyższym pliku wejściowym, działa to jako urok. Ale kiedy robię to na odwrót, tj. Zastępując „abcd” przez „ram”, niektóre śmieciowe postacie pozostają na końcu.

Zamiana „abcd” na „ram”

hi this is ram hi this is ram
This is dummy text file.
This is how search and replace works rambcd
Shriram
źródło
Czy możesz być bardziej konkretny, mówiąc „niektóre śmieciowe postacie pozostały na końcu”, co widzisz?
Burhan Khalid
Zaktualizowałem pytanie o dane wyjściowe, co mam.
Shriram

Odpowiedzi:

241

fileinputjuż obsługuje edycję w miejscu. W stdouttym przypadku przekierowuje do pliku:

#!/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
źródło
13
Co end=''powinien zrobić argument?
egpbos
18
linema już nową linię. endjest domyślnie nową linią, end=''sprawia , że print()funkcja nie drukuje dodatkowej nowej linii
jfs
11
Nie używaj fileinput! Zamiast tego możesz napisać kod, aby zrobić to sam. Przekierowanie sys.stdout nie jest świetnym pomysłem, szczególnie jeśli robisz to bez próbowania… tak jak robi to fileinput. Jeśli zostanie zgłoszony wyjątek, standardowe wyjście może nigdy nie zostać przywrócone.
craigds
9
@craigds: źle. fileinputnie jest narzędziem do wszystkich zadań ( nic nie jest), ale w wielu przypadkach jest to właściwe narzędzie, np. do implementacji sedpodobnego filtra w Pythonie. Nie używaj śrubokręta do wbijania gwoździ.
jfs
5
Jeśli z jakiegoś powodu naprawdę chcesz przekierować stdout do pliku, nie jest to trudne lepiej fileinput(w zasadzie użyj try..finallylub menedżer kontekstu, aby upewnić się, że później przywrócisz standardowe wyjście stdout). Kod źródłowy fileinputjest dość okropnie okropny i robi kilka naprawdę niebezpiecznych rzeczy pod maską. Gdybym został napisany dzisiaj, bardzo wątpię, aby znalazł się w stdlib.
craigds
333

Jak wskazał michaelb958, nie można zastąpić danych o innej długości, ponieważ spowoduje to usunięcie pozostałych części. Nie zgadzam się z innymi plakatami sugerującymi czytanie z jednego pliku i pisanie do innego. Zamiast tego wczytywałbym plik do pamięci, naprawił dane, a następnie zapisałbym go w tym samym pliku w osobnym kroku.

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

Chyba że masz ogromny plik do pracy, który jest zbyt duży, aby załadować go do pamięci za jednym razem, lub martwisz się potencjalną utratą danych, jeśli proces zostanie przerwany podczas drugiego etapu, w którym zapisujesz dane do pliku.

Jack Aidley
źródło
5
with file = open(..):nie jest prawidłową funkcją Python ( =), chociaż zamiar jest jasny. .replace()nie modyfikuje ciągu (jest niezmienny), więc musisz użyć zwróconej wartości. W każdym razie kod obsługujący duże pliki może być jeszcze prostszy, chyba że musisz wyszukać i zamienić tekst obejmujący wiele wierszy.
jfs
40
Masz całkowitą rację, a to - ludzie - powinieneś przetestować swój kod, zanim wstydzisz się w Internecie;)
Jack Aidley
19
@JonasStein: Nie, nie powinno. withOświadczenie automatycznie zamyka plik na końcu bloku instrukcji.
Jack Aidley
2
@JackAidley to interesujące. Dziękuję za wyjaśnienie.
Jonas Stein,
4
@JackAidley, ponieważ jest krótki, prosty, łatwy w użyciu i zrozumiały oraz rozwiązuje prawdziwy problem, który ma wiele osób (i dlatego wiele osób szuka - w ten sposób znajduje odpowiedź).
Ben Barden,
52

Jak napisał Jack Aidley i JF Sebastian, ten kod nie będzie działał:

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

Ale ten kod BĘDZIE działał (przetestowałem go):

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

Dzięki tej metodzie filein i fileout mogą być tym samym plikiem, ponieważ Python 3.3 nadpisze plik po otwarciu do zapisu.

Neamerjell
źródło
9
Myślę, że różnica jest tutaj: filedata.replace ('ram', 'abcd') W porównaniu do: newdata = filedata.replace ("stare dane", "nowe dane") Nie ma nic wspólnego z instrukcją "z"
Diegomanas
5
1. dlaczego miałbyś usunąć with-statement? 2. Jak stwierdzono w mojej odpowiedzi, fileinputmoże działać w miejscu - może zastępować dane w tym samym pliku (używa pliku tymczasowego wewnętrznie). Różnica polega na tym, fileinputże nie wymaga ładowania całego pliku do pamięci.
jfs
8
Aby uratować innych przed ponownym przeglądaniem odpowiedzi Jacka Aidleya, poprawiono ją od tej odpowiedzi, więc ta jest teraz zbędna (i gorsza z powodu utraty starszych withbloków).
Chris
46

Możesz dokonać takiej wymiany

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
źródło
7

Możesz także użyć 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
źródło
Dzięki Yuya. Powyższe rozwiązanie działało dobrze. Uwaga: najpierw należy wykonać kopię zapasową oryginalnego pliku, ponieważ sam zastępuje on oryginalny plik. Jeśli chcesz wielokrotnie zamieniać tekst, możesz dodawać ostatnie 2 wiersze, jak poniżej. text = text.replace (text_to_search, replace_text) path.write_text (text)
Nages
3

Za pomocą pojedynczego z blokiem możesz wyszukiwać i zamieniać tekst:

with open('file.txt','r+') as f:
    filedata = f.read()
    filedata = filedata.replace('abc','xyz')
    f.truncate(0)
    f.write(filedata)
iknowitwasyoufredo
źródło
1
Zapomniałeś seekna początku pliku przed jego zapisaniem. truncatenie robi tego, więc będziesz mieć śmieci w pliku.
ur.
2

Twój problem wynika z odczytu i zapisu do tego samego pliku. Zamiast otwierać fileToSearchdo pisania, otwórz rzeczywisty plik tymczasowy, a następnie po zakończeniu i zamknięciu tempFileużyj, os.renameaby przenieść nowy plik fileToSearch.

icktoofay
źródło
1
Friendly FYI (prosimy o edycję w odpowiedzi): Główną przyczyną nie jest możliwość skrócenia środka pliku na miejscu. Oznacza to, że jeśli szukasz 5 znaków i zastąpisz je 3, pierwsze 3 znaki z 5 wyszukiwanych znaków zostaną zastąpione; ale pozostałych 2 nie można usunąć, po prostu tam pozostaną. Plik tymczasowy usuwa te „resztki” znaków, upuszczając je zamiast zapisywać w pliku tymczasowym.
michaelb958 - GoFundMonica
2

(pip install python-util)

from pyutil import filereplace

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

Drugi parametr (rzecz do zastąpienia, np. „Abcd” może być również wyrażeniem regularnym)
Zastąpi wszystkie wystąpienia

MisterL2
źródło
Miałem z tym złe doświadczenia (dodało to kilka znaków na końcu pliku), więc nie mogę tego polecić, chociaż fajna byłaby jedna linijka.
Azrael3000
@ Azrael3000 Dodano znaki? Nie widziałem, żeby mi się to przydarzyło. Byłbym bardzo wdzięczny, gdybyś otworzył jakiś
MisterL2
1

Mój wariant, jedno słowo na raz w całym pliku.

Przeczytałem to w pamięci.

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
źródło
0

Ja to zrobiłem:

#!/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
źródło
Smutne, ale fileinput nie współpracuję inplace=Truez utf-8.
Sergio
0

Lekko zmodyfikowałem post Jayram Singha, aby zastąpić każde wystąpienie „!” znak do liczby, którą chciałem zwiększać z każdą instancją. Pomyślałem, że może to być pomocne dla kogoś, kto chciał zmodyfikować znak, który pojawiał się więcej niż raz w linii i chciał iterować. Mam nadzieję, że komuś pomoże. PS - Jestem bardzo nowy w kodowaniu, więc przepraszam, jeśli mój post jest w jakikolwiek sposób nieodpowiedni, ale to zadziałało dla mnie.

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
źródło
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
źródło
Ten kod zastąpi słowo, które zamierzasz. Jedynym problemem jest to, że przepisuje cały plik. może utknąć, jeśli plik jest zbyt długi, aby procesor mógł go obsłużyć.
Vinit Pillai
0

Tak jak:

def find_and_replace(file, word, replacement):
  with open(file, 'r+') as f:
    text = f.read()
    f.write(text.replace(word, replacement))
Cyril Alohan
źródło
Upewnij się, że twoja odpowiedź poprawia się w stosunku do innych odpowiedzi już obecnych w tym pytaniu.
hongsy
To doda tekst z zastąpieniem na końcu pliku, moim zdaniem @Jack Aidley aswer jest właśnie tym, co OP oznaczało 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.
źródło