Поиск и замена строки в файле в Python

Я хочу перебрать содержимое текстового файла и выполнить поиск и заменить на некоторых строках и записать результат обратно в файл. Я мог сначала загрузить весь файл в память, а затем записать его обратно, но это, вероятно, не самый лучший способ сделать это.

Каков наилучший способ сделать это в следующем коде?

f = open(file)
for line in f:
    if line.contains('foo'):
        newline = line.replace('foo', 'bar')
        # how to write this newline back to the file

Ответ 1

Я думаю, что-то вроде этого должно это сделать. Он в основном записывает содержимое в новый файл и заменяет старый файл новым файлом:

from tempfile import mkstemp
from shutil import move
from os import fdopen, remove

def replace(file_path, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    with fdopen(fh,'w') as new_file:
        with open(file_path) as old_file:
            for line in old_file:
                new_file.write(line.replace(pattern, subst))
    #Remove original file
    remove(file_path)
    #Move new file
    move(abs_path, file_path)

Ответ 2

Самый короткий путь, вероятно, будет заключаться в использовании fileinput module. Например, следующее добавляет номера строк в файл, на месте:

import fileinput

for line in fileinput.input("test.txt", inplace=True):
    print "%d: %s" % (fileinput.filelineno(), line),

Что происходит здесь:

  • Исходный файл перемещается в резервный файл
  • Стандартный вывод перенаправляется на исходный файл в цикле
  • Таким образом, любые операторы print записывают обратно в исходный файл

fileinput имеет больше колоколов и свистов. Например, он может использоваться для автоматической работы со всеми файлами в sys.args[1:], без необходимости явно перебирать их. Начиная с Python 3.2, он также предоставляет удобный менеджер контекстов для использования в инструкции with.


В то время как fileinput отлично подходит для сценариев throwaway, я бы с осторожностью использовал его в реальном коде, потому что, по общему признанию, он не очень читабельна или знакома. В реальном (производственном) коде стоит потратить еще несколько строк кода, чтобы сделать процесс явным и, таким образом, сделать читаемый код.

Есть два варианта:

  • Файл не слишком большой, и вы можете просто прочитать его полностью в памяти. Затем закройте файл, откройте его в режиме записи и напишите измененное содержимое обратно.
  • Файл слишком велик для хранения в памяти; вы можете переместить его во временный файл и открыть его, читая его по строкам, записывая обратно в исходный файл. Обратите внимание, что для этого требуется в два раза больше хранилища.

Ответ 3

Вот еще один пример, который был протестирован и будет соответствовать шаблонам поиска и замены:

import fileinput
import sys

def replaceAll(file,searchExp,replaceExp):
    for line in fileinput.input(file, inplace=1):
        if searchExp in line:
            line = line.replace(searchExp,replaceExp)
        sys.stdout.write(line)

Пример использования:

replaceAll("/fooBar.txt","Hello\sWorld!$","Goodbye\sWorld.")

Ответ 4

Это должно работать: (редактирование на месте)

import fileinput

# Does a list of files, and
# redirects STDOUT to the file in question
for line in fileinput.input(files, inplace = 1): 
      print line.replace("foo", "bar"),

Ответ 5

На основании ответа Томаса Уотнедала. Тем не менее, это точно не отвечает на прямую часть исходного вопроса. Функция все равно может быть заменена на основе строки

Эта реализация заменяет содержимое файла без использования временных файлов, поскольку в результате права на файлы остаются неизменными.

Также re.sub вместо замены, позволяет заменять регулярное выражение вместо простой замены текста.

Чтение файла как отдельной строки вместо строки за строкой позволяет выполнять многострочное совпадение и замену.

import re

def replace(file, pattern, subst):
    # Read contents from file as a single string
    file_handle = open(file, 'r')
    file_string = file_handle.read()
    file_handle.close()

    # Use RE package to allow for replacement (also allowing for (multiline) REGEX)
    file_string = (re.sub(pattern, subst, file_string))

    # Write contents to file.
    # Using mode 'w' truncates the file.
    file_handle = open(file, 'w')
    file_handle.write(file_string)
    file_handle.close()

Ответ 6

Как предлагает lassevk, напишите новый файл, как вы идете, вот пример кода:

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()

Ответ 7

Если вам нужна общая функция, которая заменяет любой текст каким-либо другим текстом, это, вероятно, лучший способ, особенно если вы поклонник регулярных выражений:

import re
def replace( filePath, text, subs, flags=0 ):
    with open( filePath, "r+" ) as file:
        fileContents = file.read()
        textPattern = re.compile( re.escape( text ), flags )
        fileContents = textPattern.sub( subs, fileContents )
        file.seek( 0 )
        file.truncate()
        file.write( fileContents )

Ответ 8

Более питоническим способом будет использование контекстных менеджеров, таких как код ниже:

from tempfile import mkstemp
from shutil import move
from os import remove

def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()
    with open(target_file_path, 'w') as target_file:
        with open(source_file_path, 'r') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

Вы можете найти полный фрагмент здесь.

Ответ 9

Создайте новый файл, скопируйте строки из старого в новый и замените его перед тем, как писать строки в новый файл.

Ответ 10

Расширение ответа @Kiran, которое я согласен, более кратким и Pythonic, добавляет кодеки для поддержки чтения и записи UTF-8:

import codecs 

from tempfile import mkstemp
from shutil import move
from os import remove


def replace(source_file_path, pattern, substring):
    fh, target_file_path = mkstemp()

    with codecs.open(target_file_path, 'w', 'utf-8') as target_file:
        with codecs.open(source_file_path, 'r', 'utf-8') as source_file:
            for line in source_file:
                target_file.write(line.replace(pattern, substring))
    remove(source_file_path)
    move(target_file_path, source_file_path)

Ответ 11

Используя hamishmcn answer в качестве шаблона, я смог найти строку в файле, которая соответствует моему регулярному выражению и заменяет его пустой строкой.

import re 

fin = open("in.txt", 'r') # in file
fout = open("out.txt", 'w') # out file
for line in fin:
    p = re.compile('[-][0-9]*[.][0-9]*[,]|[-][0-9]*[,]') # pattern
    newline = p.sub('',line) # replace matching strings with empty string
    print newline
    fout.write(newline)
fin.close()
fout.close()

Ответ 12

если вы удалите отступ, как показано ниже, он будет искать и заменять в нескольких строках. См. Ниже, например.

def replace(file, pattern, subst):
    #Create temp file
    fh, abs_path = mkstemp()
    print fh, abs_path
    new_file = open(abs_path,'w')
    old_file = open(file)
    for line in old_file:
        new_file.write(line.replace(pattern, subst))
    #close temp file
    new_file.close()
    close(fh)
    old_file.close()
    #Remove original file
    remove(file)
    #Move new file
    move(abs_path, file)

Ответ 13

fileinput довольно просто, как упоминалось в предыдущих ответах:

import fileinput

def replace_in_file(file_path, search_text, new_text):
    with fileinput.input(file_path, inplace=True) as f:
        for line in f:
            new_line = line.replace(search_text, new_text)
            print(new_line, end='')

Объяснение:

  • fileinput может принимать несколько файлов, но я предпочитаю закрывать каждый отдельный файл, как только он обрабатывается. Таким образом, помещается один file_path в оператор with.
  • Оператор print ничего не печатает, когда inplace=True, потому что STDOUT пересылается в исходный файл.
  • end='' в выражении print предназначено для удаления промежуточных пустых новых строк.

Может использоваться следующим образом:

file_path = '/path/to/my/file'
replace_in_file(file_path, 'old-text', 'new-text')