Як шукати та замінювати текст у файлі?


212

Як шукати та замінювати текст у файлі за допомогою Python 3?

Ось мій код:

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

Вхідний файл:

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

Коли я шукаю та замінюю 'ram' на 'abcd' у вхідному файлі, він працює як шарм. Але коли я це роблю навпаки, тобто замінюю 'abcd' на 'ram', деякі непотрібні символи залишаються в кінці.

Заміна "abcd" на "ram"

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

Чи можете ви бути дещо конкретнішими, коли говорите, "якісь непотрібні символи залишаються в кінці кінців", що ви бачите?
Бурхан Халід

Оновили питання з результатами, що я отримав.
Шрірам

Відповіді:


241

fileinputвже підтримує редагування на місці. У stdoutцьому випадку він переспрямовує до файлу:

#!/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
Який end=''аргумент повинен зробити?
egpbos

18
lineвже має новий рядок. endє end=''print()
новим

11
Не використовуйте введення файлів! Подумайте написати код, щоб зробити це самостійно. Перенаправлення sys.stdout не є чудовою ідеєю, особливо якщо ви робите це без спроби. Нарешті, як і файлinput. Якщо виняток буде підвищений, ваш stdout ніколи не буде відновлений.
craigds

9
@craigds: неправильно. fileinputне є інструментом для всіх робочих місць ( нічого не їсти) , але є багато випадків , коли це є правильним інструментом , наприклад, реалізувати sed-like фільтр в Python. Не використовуйте викрутку для набивання цвяхів.
jfs

5
Якщо ви дійсно хочете з якоїсь причини перенаправити stdout у свій файл, це не важко зробити це краще, ніж fileinputце (в основному, використовуйте try..finallyабо менеджер контексту, щоб переконатися, що після цього ви повернете stdout до початкового значення). Вихідний код для цього fileinputнадзвичайно жахливий, і він робить дуже небезпечні речі під кришкою. Якби це було написано сьогодні, я дуже сумніваюся, що це ввійшло б у stdlib.
Craigds

333

Як вказував michaelb958, ви не можете замінити на місці дані різної довжини, оскільки це виведе решту розділів на місце. Я не згоден з іншими плакатами, пропонуючи вам читати з одного файлу і писати в інший. Натомість я прочитав би файл у пам'яті, виправляв дані, а потім виписував їх у той самий файл на окремому кроці.

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

Якщо ви не маєте масивного файлу, для якого занадто великий, щоб за один раз завантажуватись у пам'ять, або ви переживаєте про можливу втрату даних, якщо процес буде перервано під час другого кроку, коли ви записуєте дані у файл.


5
with file = open(..):недійсний Python ( =), хоча намір зрозумілий. .replace()не змінює рядок (він незмінний), тому вам потрібно використовувати повернене значення. У будь-якому випадку код, який підтримує великі файли, може бути ще простішим, якщо вам не потрібно шукати та замінювати текст, який охоплює кілька рядків.
jfs

40
Ви абсолютно праві, і це - люди, - тому ви повинні перевірити свій код, перш ніж засмутити себе в Інтернеті;)
Джек Едлі

19
@JonasStein: Ні, не повинно. Оператор withавтоматично закриває файл в кінці блоку операторів.
Джек Едлі

2
@JackAidley, що цікаво. Дякую за пояснення.
Джонас Штейн

4
@JackAidley тому, що він короткий, простий, легко використовується і зрозумілий, і вирішує справжню проблему, яку має багато людей (і, отже, багато людей шукає - таким чином, знаходячи вашу відповідь).
Бен Барден

52

Як опублікував Джек Едлі та зазначив Дж. Ф. Себастьян, цей код не працює:

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

Але цей код спрацює (я його перевірив):

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

За допомогою цього методу filein та fileout можуть бути одним і тим же файлом, оскільки Python 3.3 замінить файл при відкритті для запису.


9
Я вважаю, різниця тут: filedata.replace ('ram', 'abcd') У порівнянні з: newdata = filedata.replace ("старі дані", "нові дані") Нічого спільного з оператором "з"
Diegomanas

5
1. чому б ви видалили withзаяву? 2. Як зазначено у моїй відповіді, fileinputможе працювати на місці - він може замінити дані в одному файлі (він використовує тимчасовий файл внутрішньо). Різниця полягає в тому, fileinputщо не потрібно завантажувати весь файл в пам'ять.
jfs

8
Тільки для того, щоб врятувати інших, переглядаючи відповідь Джека Едлі, вона була виправлена ​​після цієї відповіді, тому ця тепер є зайвою (і поступається через втрату чистіших withблоків).
Кріс

46

Ви можете зробити заміну так

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

Ви також можете використовувати 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)

Дякую Юю. Вищевказане рішення спрацювало добре. Примітка. Спочатку потрібно взяти резервну копію оригінального файлу, оскільки він замінює сам вихідний файл. Якщо ви хочете неодноразово замінювати текст, ви можете продовжувати додавати останні два рядки, як показано нижче. text = text.replace (text_to_search, substitu_text) path.write_text (text)
Nages

3

За допомогою синглу з блоком ви можете шукати та замінювати текст:

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

1
Ви забули на seekпочатку файлу, перш ніж писати його. truncateне робить цього, і тому у вас буде сміття у файлі.
ур.

2

Ваша проблема пов'язана з читанням і записом у той самий файл. Замість того, щоб відкривати fileToSearchдля написання, відкрийте фактичний тимчасовий файл, а потім, коли ви закінчите і закриєте tempFile, використовуйте os.renameдля переміщення нового файлу fileToSearch.


1
Дружній FYI (не соромтеся редагувати у відповідь): Першопричина не в змозі скоротити середину файлу на місці. Тобто, якщо ви будете шукати 5 символів і замінити 3, перші 3 символи з 5 шуканих заміняться; але інші 2 неможливо видалити, вони просто залишаться там. Тимчасове рішення файлу видаляє ці символи, що залишилися, скидаючи їх, а не записуючи їх у тимчасовий файл.
michaelb958 - GoFundMonica

2

(pip встановити python-util)

from pyutil import filereplace

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

Другий параметр (те, що потрібно замінити, наприклад, "abcd" також може бути регулярним виразом)
замінить усі випадки


У мене був такий поганий досвід з цим (він додав деяких символів до кінця файлу), тому я не можу його рекомендувати, навіть якщо одноклапник був би непоганим.
Azrael3000

@ Azrael3000 Додано символів? Я не бачив, щоб це сталося зі мною. Я дуже вдячний, якщо ви відкрили проблему про Github, щоб я міг її виправити github.com/MisterL2/python-util
MisterL2

1

Мій варіант, по одному слову по всьому файлу.

Я читав це на пам'ять.

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

Я зробив це:

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

Сумно, але fileinput НЕ Doen робота з inplace=Trueз utf-8.
Серхіо

0

Я трохи змінив пост Джейрама Сінгха, щоб замінити кожен екземпляр "!" символу до числа, яке я хотів збільшити з кожним екземпляром. Думав, що це може бути корисно тому, хто хотів змінити символ, що траплявся не раз у рядку та хотів повторити його. Сподіваюся, що хтось допомагає. PS - Я дуже новачка в кодуванні, тому вибачтесь, якщо моя публікація в будь-якому разі невідповідна, але це працювало для мене.

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

Цей код замінить слово, яке ви маєте намір. Єдина проблема полягає в тому, що він переписує весь файл. може застрягнути, якщо файл занадто довгий для обробки процесора.
Вініт Піллай

0

Так:

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

Будь ласка, переконайтеся, що ваша відповідь покращується в порівнянні з іншими відповідями, вже наявними в цьому питанні.
hongsy

Це додасть текст із заміною до кінця файлу, на мою думку @Jack Aidley aswer - це саме те, що означав ОП stackoverflow.com/a/17141572/6875391
Кирилл

-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) 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.