私は Python を使用しており、ファイルを削除またはコピーせずに文字列をテキスト ファイルに挿入したいと考えています。どうやってやるの?
8 に答える
残念ながら、ファイルを書き直さずにファイルの途中に挿入する方法はありません。以前のポスターが示したように、ファイルに追加したり、シークを使用してその一部を上書きしたりできますが、最初または途中に何かを追加したい場合は、それを書き直す必要があります。
これはオペレーティング システムの問題であり、Python の問題ではありません。それはどの言語でも同じです。
私が通常行うことは、ファイルから読み取り、変更を加えて、myfile.txt.tmp などと呼ばれる新しいファイルに書き出すことです。ファイルが大きすぎる可能性があるため、これはファイル全体をメモリに読み込むよりも優れています。一時ファイルが完成したら、元のファイルと同じ名前に変更します。
これは、何らかの理由でファイルの書き込みがクラッシュまたは中止された場合でも、元のファイルが変更されていないため、これを行うのに適した安全な方法です。
何をしたいかによります。追加するには、「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
fileinputinplace=1 パラメータを使用すると、Python 標準ライブラリのモジュールはファイルをその場で書き換えます。
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
ファイルをその場で書き換えるには、多くの場合、古いコピーを名前を変更して保存します。Unix の人々は~、古いものをマークするために を追加します。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 + "~")
Python の mmap モジュールを使用すると、ファイルに挿入できます。次のサンプルは、Unix で実行する方法を示しています (Windows の mmap は異なる場合があります)。これはすべてのエラー状態を処理するわけではなく、元のファイルが破損したり失われたりする可能性があることに注意してください。また、これはユニコード文字列を処理しません。
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 までのファイルの内容を読み取って一時的に保存する必要があるため、不便で効率的ではありません。巨大になる。
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()
これをきれいに行うための小さなクラスを作成しました。
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