回答:
私はこのような何かがそれをするべきだと思います。基本的にはコンテンツを新しいファイルに書き込み、古いファイルを新しいファイルに置き換えます。
from tempfile import mkstemp
from shutil import move, copymode
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))
#Copy the file permissions from the old file to the new file
copymode(file_path, abs_path)
#Remove original file
remove(file_path)
#Move new file
move(abs_path, file_path)
mkstemp()
2タプルを返していることを発見しました(fh, abs_path) = fh, abs_path
。質問したとき、そのことを知りませんでした。
最も短い方法は、おそらくfileinputモジュールを使用することでしょう。たとえば、次の例では、行番号をファイルにインプレースで追加しています。
import fileinput
for line in fileinput.input("test.txt", inplace=True):
print('{} {}'.format(fileinput.filelineno(), line), end='') # for Python 3
# print "%d: %s" % (fileinput.filelineno(), line), # for Python 2
ここで何が起こるか:
print
ステートメントは元のファイルに書き戻しますfileinput
より多くの鐘と笛を持っています。たとえば、sys.args[1:]
明示的に反復することなく、のすべてのファイルを自動的に操作するために使用できます。Python 3.2からは、with
ステートメントで使用するための便利なコンテキストマネージャも提供しています。
ながら fileinput
使い捨てスクリプトのための素晴らしいです確かに、それは非常に読みやすいか、慣れていないですので、私は実際のコードでそれを使用しての警戒するでしょう。実際の(本番)コードでは、プロセスを明示的にしてコードを読み取り可能にするために、数行のコードを費やすだけの価値があります。
2つのオプションがあります。
print(line, end='')
以下はテスト済みの別の例で、検索と置換のパターンに一致します。
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.")
searchExp in line
もline.replace
ありません。確かに使用例は間違っています。
if searchExp in line: line = line.replace(searchExp, replaceExpr)
あなたの代わりにただ書くことができますline = line.replace(searchExp, replaceExpr)
。例外は生成されず、ラインは変更されません。
sys.stdout.write(line)
。再度、感謝します!
これは動作するはずです:(インプレース編集)
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"),
Thomas Watnedalの回答に基づく。ただし、これは元の質問の行ごとの部分に正確に答えるものではありません。関数は引き続き行ごとに置き換えることができます
この実装では、一時ファイルを使用せずにファイルの内容を置き換えます。その結果、ファイルのアクセス許可は変更されません。
また、置換の代わりにre.subを使用すると、プレーンテキストの代わりに正規表現の置換のみが可能になります。
ファイルを1行ずつではなく1つの文字列として読み取ると、複数行の一致と置換が可能になります。
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()
rb
とwb
属性を使用すると、元の行末が維持されます
任意のテキストを他のテキストに置き換える汎用関数が必要な場合、これはおそらく正規表現のファンである場合に最適な方法です。
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()
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
ステートメントに1つ配置されます。print
は元のファイルに転送されるinplace=True
ため、ステートメントは何も出力しませんSTDOUT
。end=''
でprint
声明中間空白新しい行を排除することです。次のように使用できます。
file_path = '/path/to/my/file'
replace_in_file(file_path, 'old-text', 'new-text')
以下のようにインデントを削除すると、複数行で検索して置換されます。例については、以下を参照してください。
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)
file
同じ名前の事前定義されたクラスを隠しています。