Как искать и заменять текст в файле?


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
Не используйте fileinput! Попробуйте написать код, чтобы сделать это самостоятельно. Перенаправление sys.stdout не очень хорошая идея, особенно если вы делаете это без попытки ... в конце концов, как это делает fileinput. Если возникает исключение, ваш стандартный вывод никогда не будет восстановлен.
Крейгдс

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

5
Если вы действительно хотите по какой-то причине перенаправить stdout в свой файл, это не сложно сделать лучше, чем это fileinputделает (в основном, используйте try..finallyили contextmanager, чтобы гарантировать, что впоследствии вы вернете stdout к его первоначальному значению). Исходный код fileinputдовольно устрашающе ужасен, и он делает некоторые действительно небезопасные вещи под капотом. Если бы он был написан сегодня, я очень сомневаюсь, что он превратился бы в stdlib.
Крейгдс

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

Как писал Джек Эйдли и JF Себастьян, этот код не будет работать:

 # 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 ("старые данные", "новые данные") Не имеет ничего общего с выражением "with"
Диегоманас

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)

Спасибо, Юя. Вышеуказанное решение сработало хорошо. Примечание. Сначала необходимо создать резервную копию исходного файла, поскольку он заменяет сам исходный файл. Если вы хотите многократно заменять текст, можете добавить последние 2 строки, как показано ниже. Текст = text.replace (text_to_search, replacement_text) path.write_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 install 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 не работаю 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))

Пожалуйста, убедитесь, что ваш ответ улучшается по сравнению с другими ответами, уже присутствующими в этом вопросе.
гонг

Это добавит текст с заменой в конец файла, на мой взгляд, @Jack Aidley aswer - это то, что OP означало 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.