Поиск и замена строки в файле в 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
Решение
Я думаю, что-то подобное должно сделать это. Он в основном записывает содержимое в новый файл и заменяет старый файл новым файлом:
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)
Другие советы
Самый короткий путь, вероятно, заключается в использовании модуля fileinput а>. Например, следующий код добавляет номера строк в файл на месте:
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
отлично подходит для одноразовых скриптов, я бы с осторожностью использовал его в реальном коде, потому что, по общему признанию, он не очень читабелен или знаком. В реальном (производственном) коде стоит потратить всего несколько строк кода, чтобы сделать процесс явным и, следовательно, сделать код читабельным.
Есть два варианта:
<Ол>Вот еще один пример, который был протестирован и будет соответствовать поиску & amp; заменить шаблоны:
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!<*>quot;,"Goodbye\sWorld.")
Это должно работать: (редактирование на месте)
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"),
На основании ответа Томаса Ватнедала. Тем не менее, это не дает точного ответа на прямую часть исходного вопроса. Функция все еще может заменять построчно
Эта реализация заменяет содержимое файла без использования временных файлов, в результате чего права доступа к файлам остаются неизменными.
Также re.sub вместо replace, разрешает замену регулярных выражений вместо простой замены текста.
Чтение файла в виде одной строки вместо строки за строкой позволяет выполнять многострочное сопоставление и замену.
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()
Как предлагает lassevk, запишите новый файл по ходу работы, вот пример кода:
fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()
Если вам нужна универсальная функция, которая заменяет текст любого другим текстом, это, вероятно, лучший способ, особенно если вы поклонник регулярных выражений:
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 )
Более подходящим способом было бы использовать контекстные менеджеры, подобные приведенному ниже коду:
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)
Вы можете найти полный фрагмент здесь .
Создайте новый файл, скопируйте строки из старого в новый и выполните замену, прежде чем записывать строки в новый файл.
Расширяя ответ @ 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)
Используя ответ hamishmcn в качестве шаблона, я смог найти в файле строку, соответствующую моему регулярному выражению, и заменить ее пустой строкой.
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()
если вы удалите отступ, как показано ниже, он будет искать и заменять в несколько строк. См. Ниже, например.
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)
Для пользователей Linux:
import os
os.system('sed -i \'s/foo/bar/\' '+file_path)