テキストファイルを変更する方法


175

Pythonを使用していますが、ファイルを削除またはコピーせずにテキストファイルに文字列を挿入したいと考えています。どうやってやるの?


1
この回答はAlex Martelli が参照できます。
Alok、2014



@Ani他の投稿テキストファイルの指定された位置に行挿入することの複製であり、確かにここに明確に構成された回答があります。他の方法の代わりにここに回答を追加してみませんか?受け入れられた回答は、良い質問の要件ではありません
Bhargav Rao

@BhargavRao投票が撤回されました。私はその重複を見つけたはずです!
Ani Menon

回答:


134

残念ながら、ファイルを書き直さずにファイルの途中に挿入する方法はありません。以前の投稿者が示したように、ファイルに追加したり、シークを使用してファイルの一部を上書きしたりできますが、最初または途中に何かを追加したい場合は、ファイルを書き換える必要があります。

これはオペレーティングシステムであり、Pythonではありません。すべての言語で同じです。

私が通常行うことは、ファイルから読み取って変更を加え、myfile.txt.tmpという名前の新しいファイルなどに書き出すことです。これは、ファイルが大きすぎるために、ファイル全体をメモリに読み込むよりも優れています。一時ファイルが完成したら、元のファイルと同じ名前に変更します。

これは、ファイルの書き込みが何らかの理由でクラッシュまたは異常終了した場合でも、元のファイルがそのまま残っているため、安全で適切な方法です。


3
awk / sedのようなUNIXツールは、コードで同様のことをしますか?
Manish Gill

これがすべての言語で同じであるとは限りません。ActionScriptの場合:fileStream.openAsync(filename、FileMode.UPDATE); 次に、必要なファイルのどこにでも移動して、何でも変更できます。
AndrewBenjamin 2014

2
@AndrewBenjamin ActionScriptが作成しているシステムコールを知っていますか?openAsyncがファイルを読み取り、呼び出し後に新しいファイルを書き込む可能性はありますか?
AlexLordThorsen 14

@Rawrgulmuffins私はしません。ただし、数GBのファイルサイズを処理するために使用したため、ファイル全体をメモリに読み取っていないことがわかります。C#ストリームライターで書くのと同じだと思います。大規模な開発やファイル操作ではなく、小さなことをすばやく行うためのツールとしてPythonを考えています。
AndrewBenjamin 2015年

4
@AndrewBenjamin、ユーザーはファイル内を探して変更することを求めていません(私が知っているすべての言語でそれを行うことができます)。彼はテキストを挿入することについて尋ねています。これは、単にファイルに既にあるものを単に変更/上書きすることとは異なります。多分実際のアプリケーションでは異なるかもしれませんが、ActionScript APIで見つけることができないことは、この点で他の言語と動作が異なることを示していません。
エストラーダ

104

何をしたいかによります。追加するには、「a」で開くことができます。

 with open("foo.txt", "a") as f:
     f.write("new line\n")

何かを解釈したい場合は、最初にファイルから読み取る必要があります。

with open("foo.txt", "r+") as f:
     old = f.read() # read everything in the file
     f.seek(0) # rewind
     f.write("new line\n" + old) # write the new line before

9
ほんの少しの追加withですが、Python 2.5でステートメントを使用するには、「from future import with_statement」を追加する必要があります。それ以外は、withステートメントを使用してファイルを開く方が、手動で閉じるよりも確実に読みやすく、エラーが発生しにくくなります。
Alexander Kojevnikov 2008

2
arg fileinputを使用する場合、ダーティオープン/読み取り/変更/書き込み/置換ルーチンを適切に処理するヘルパーlibを検討してくださいinline=True。ここの例:stackoverflow.com/a/2363893/47390
mikegreenberg

3
ファイルを閉じることを忘れないでください。f.Close()
D.Rosado

5
D.Rosadoは私が使用するスタイルではありませんが、withスタイルを使用する場合、手動で閉じる必要はないと思います。withは、作成したリソースを追跡します。
クリス

4
あなたはしていない手動で近いファイルにする必要があります。これが、ここで「with」を使用する全体のポイントです。(まあ、実際には、Pythonはファイルオブジェクトがガベージコレクションされるとすぐにこれを行います。CPythonでは、バインドされた名前がスコープから外れたときに発生します...しかし、他の実装はそうではなく、CPythonはいつかそれをやめるかもしれませんので、推奨されている「と」)
ユルゲンA.エアハード

71

fileinputPython標準ライブラリのモジュールは、inplace = 1パラメータを使用すると、ファイルをインプレースで書き換えます。

import sys
import fileinput

# replace all occurrences of 'sit' with 'SIT' and insert a line after the 5th
for i, line in enumerate(fileinput.input('lorem_ipsum.txt', inplace=1)):
    sys.stdout.write(line.replace('sit', 'SIT'))  # replace 'sit' and write
    if i == 4: sys.stdout.write('\n')  # write a blank line after the 5th line

1
これはpython3でどのように機能すると予想されますか?このようなコードが含まれるアプリをpythonからpython3に移植したところ、これをまったく機能させることができませんでした。'line'変数はバイトタイプです。Unicodeにデコードしてから変更し、エンコードしてバイトに戻しましたが、正しく機能しませんでした。頭上で覚えられない例外が発生しました。人々はpython3でfileinput inplace = 1を使用して成功していますか?
robru


13
しかし、問題のないファイルを最初に重要でないファイルでテストしましたよね?
Paula Livingstone

33

ファイルを所定の場所に再書き込みするには、古いコピーを変更した名前で保存することがよくあります。Unixの人々~は古いものをマークするためにa を追加します。Windowsの人々は、.bakや.oldを追加したり、ファイル全体の名前を変更したり、名前の前に〜を付けたりして、あらゆることを行います。

import shutil
shutil.move( afile, afile+"~" )

destination= open( aFile, "w" )
source= open( aFile+"~", "r" )
for line in source:
    destination.write( line )
    if <some condition>:
        destination.write( >some additional line> + "\n" )
source.close()
destination.close()

の代わりにshutil、以下を使用できます。

import os
os.rename( aFile, aFile+"~" )

1
いいね。.readlines()がソースの反復よりも優れているかどうか疑問に思いますか?
bozdoz 2013

2
@bozdoz:readlinesはファイル全体を読み取るため、反復処理の方が優れています。大きなファイルには適していません。もちろん、これは、ローカライズされた方法で変更を行えることを前提としています。できない場合や、コードがはるかに複雑になる場合があります。
ユルゲン・A.エアハルト

@ S.Lott:os.rename(aFile, aFile + "~")コピーを作成するのではなく、ソースファイルの名前を変更します。
Patapoom

14

Pythonのmmapモジュールを使用すると、ファイルに挿入できます。次のサンプルは、Unixでの実行方法を示しています(Windows mmapは異なる場合があります)。これはすべてのエラー状態を処理するわけではなく、元のファイルが破損または失われる可能性があることに注意してください。また、これはUnicode文字列を処理しません。

import os
from mmap import mmap

def insert(filename, str, pos):
    if len(str) < 1:
        # nothing to insert
        return

    f = open(filename, 'r+')
    m = mmap(f.fileno(), os.path.getsize(filename))
    origSize = m.size()

    # or this could be an error
    if pos > origSize:
        pos = origSize
    elif pos < 0:
        pos = 0

    m.resize(origSize + len(str))
    m[pos+len(str):] = m[pos:origSize]
    m[pos:pos+len(str)] = str
    m.close()
    f.close()

「r +」モードで開いたファイルでmmapを使用せずにこれを行うこともできますが、挿入位置からEOFにファイルの内容を読み取り、一時的に保存する必要があるため、利便性と効率が低下します。巨大になる。


14

Adamが述べたように、メモリにすべてを読み取るのに十分なメモリがあるかどうかを判断する前に、システムの制限を考慮に入れて、その一部を置き換えて再書き込みする必要があります。

小さなファイルを処理している場合、またはメモリの問題がない場合、これが役立つ場合があります。

オプション1) ファイル全体をメモリに読み込み、行全体または一部を正規表現に置き換え、その行と追加の行で置き換えます。「中央の行」がファイル内で一意であることを確認する必要があります。または、各行にタイムスタンプがある場合、これはかなり信頼できるはずです。

# open file with r+b (allow write and binary mode)
f = open("file.log", 'r+b')   
# read entire content of file into memory
f_content = f.read()
# basically match middle line and replace it with itself and the extra line
f_content = re.sub(r'(middle line)', r'\1\nnew line', f_content)
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(f_content)
# close file
f.close()

オプション2) 中央の線を見つけて、その線と追加の線で置き換えます。

# open file with r+b (allow write and binary mode)
f = open("file.log" , 'r+b')   
# get array of lines
f_content = f.readlines()
# get middle line
middle_line = len(f_content)/2
# overwrite middle line
f_content[middle_line] += "\nnew line"
# return pointer to top of file so we can re-write the content with replaced string
f.seek(0)
# clear file content 
f.truncate()
# re-write the content with the updated content
f.write(''.join(f_content))
# close file
f.close()

2

これをきれいに行うために小さなクラスを書いた。

import tempfile

class FileModifierError(Exception):
    pass

class FileModifier(object):

    def __init__(self, fname):
        self.__write_dict = {}
        self.__filename = fname
        self.__tempfile = tempfile.TemporaryFile()
        with open(fname, 'rb') as fp:
            for line in fp:
                self.__tempfile.write(line)
        self.__tempfile.seek(0)

    def write(self, s, line_number = 'END'):
        if line_number != 'END' and not isinstance(line_number, (int, float)):
            raise FileModifierError("Line number %s is not a valid number" % line_number)
        try:
            self.__write_dict[line_number].append(s)
        except KeyError:
            self.__write_dict[line_number] = [s]

    def writeline(self, s, line_number = 'END'):
        self.write('%s\n' % s, line_number)

    def writelines(self, s, line_number = 'END'):
        for ln in s:
            self.writeline(s, line_number)

    def __popline(self, index, fp):
        try:
            ilines = self.__write_dict.pop(index)
            for line in ilines:
                fp.write(line)
        except KeyError:
            pass

    def close(self):
        self.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        with open(self.__filename,'w') as fp:
            for index, line in enumerate(self.__tempfile.readlines()):
                self.__popline(index, fp)
                fp.write(line)
            for index in sorted(self.__write_dict):
                for line in self.__write_dict[index]:
                    fp.write(line)
        self.__tempfile.close()

その後、次のように使用できます。

with FileModifier(filename) as fp:
    fp.writeline("String 1", 0)
    fp.writeline("String 2", 20)
    fp.writeline("String 3")  # To write at the end of the file

これは個人的には機能しません。ファイルにテキストを追加しますが、最初にすべてを削除します!
ブレットホーカー

実際、これはまったく機能しません。恥ずかしいです。良い考えのように思えました。
MarioKrušelj19年

0

UNIXを知っている場合は、次のことを試してください。

注:$はコマンドプロンプトを意味します

次のような内容のファイルmy_data.txtがあるとします。

$ cat my_data.txt
This is a data file
with all of my data in it.

次に、osモジュールを使用して、通常のsedコマンドを使用できます

import os

# Identifiers used are:
my_data_file = "my_data.txt"
command = "sed -i 's/all/none/' my_data.txt"

# Execute the command
os.system(command)

sedを知らない場合は、チェックしてください。非常に便利です。


3
それはまったくPythonicではありません
DarkSuniuM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.