344

テキスト ファイルの内容をループし、いくつかの行で検索と置換を行い、結果をファイルに書き戻したいと考えています。最初にファイル全体をメモリにロードしてから書き戻すこともできますが、おそらくそれは最善の方法ではありません。

次のコード内でこれを行う最良の方法は何ですか?

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
4

13 に答える 13

295

最短の方法は、おそらく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

ここで何が起こるか:

  1. 元のファイルはバックアップ ファイルに移動されます
  2. 標準出力は、ループ内で元のファイルにリダイレクトされます
  3. したがって、printステートメントは元のファイルに書き戻されます

fileinputより多くのベルとホイッスルがあります。たとえば、sys.args[1:]明示的に繰り返し処理することなく、 内のすべてのファイルを自動的に操作するために使用できます。Python 3.2 以降では、withステートメントで使用するための便利なコンテキスト マネージャーも提供されます。


使い捨てのスクリプトにfileinputは最適ですが、実際のコードで使用するのは慎重です。実際の (本番) コードでは、プロセスを明示的にしてコードを読みやすくするために、あと数行のコードを追加するだけの価値があります。

次の 2 つのオプションがあります。

  1. ファイルは大きすぎず、完全にメモリに読み込むことができます。次に、ファイルを閉じ、書き込みモードで再度開き、変更された内容を書き戻します。
  2. ファイルが大きすぎてメモリに格納できません。それを一時ファイルに移動して開き、行ごとに読み取り、元のファイルに書き戻すことができます。これには 2 倍のストレージが必要であることに注意してください。
于 2008-11-14T15:47:40.300 に答える
221

私はこのような何かがそれを行うべきだと思います。基本的に、コンテンツを新しいファイルに書き込み、古いファイルを新しいファイルに置き換えます。

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)
于 2008-09-02T09:42:21.037 に答える
89

テストされた別の例を次に示します。検索と置換のパターンに一致します。

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.")
于 2008-11-24T19:02:28.313 に答える
68

これはうまくいくはずです:(インプレース編集)

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"),
于 2009-09-07T10:07:25.203 に答える
24

Thomas Watnedal による回答に基づいています。ただし、これは元の質問の行ごとの部分に正確には答えません。関数は引き続き行ごとに置き換えることができます

この実装では、一時ファイルを使用せずにファイルの内容を置き換えます。その結果、ファイルのアクセス許可は変更されません。

また、replace の代わりに 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()
于 2012-11-30T08:51:17.913 に答える
18

lassevk が示唆するように、新しいファイルを書き出します。コード例を次に示します。

fin = open("a.txt")
fout = open("b.txt", "wt")
for line in fin:
    fout.write( line.replace('foo', 'bar') )
fin.close()
fout.close()
于 2008-09-02T09:42:57.213 に答える
13

任意のテキストを他のテキストに置き換える汎用関数が必要な場合、特に正規表現のファンである場合は、これが最善の方法です。

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 )
于 2014-02-18T14:43:02.213 に答える
13

より Pythonic な方法は、以下のコードのようなコンテキスト マネージャーを使用することです。

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)

完全なスニペットはこちらにあります

于 2013-09-07T18:39:01.017 に答える
4

新しいファイルを作成し、行を古いものから新しいものにコピーし、行を新しいファイルに書き込む前に置き換えます。

于 2008-09-02T09:24:20.953 に答える
2

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()
于 2014-04-17T02:13:32.717 に答える
0

以下のようにインデントを外すと、複数行で検索・置換されます。たとえば、以下を参照してください。

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)
于 2012-08-02T19:12:12.180 に答える