米国に10GBのHDDUbuntuVPSがあり(そして他の場所に住んでいます)、ハードドライブに9GBのテキストファイルがあるとします。私は512MBのRAMを持っており、ほぼ同じ量のスワップを持っています。
HDDスペースを追加できず、ファイルを別の場所に移動して処理できないという事実を考えると、Pythonを使用してファイルから一部の行を削除する効率的な方法はありますか(できれば、他の言語でもかまいません)?
これはどう?その場でファイルを編集します。いくつかの小さなテキスト ファイル (Python 2.6.1) でテストしましたが、大量のファイルでうまく動作するかどうかはわかりません。
正しく動作しなかったため、手動の EOF チェックで無期限の while ループを使用しましたfor line in f:
(おそらくすべてのジャンプが通常の反復を台無しにします)。これを確認するより良い方法があるかもしれませんが、私はPythonに比較的慣れていないので、誰かがあれば教えてください.
また、関数を定義する必要がありますisRequired(line)
。
writeLoc = 0
readLoc = 0
with open( "filename" , "r+" ) as f:
while True:
line = f.readline()
#manual EOF check; not sure of the correct
#Python way to do this manually...
if line == "":
break
#save how far we've read
readLoc = f.tell()
#if we need this line write it and
#update the write location
if isRequired(line):
f.seek( writeLoc )
f.write( line )
writeLoc = f.tell()
f.seek( readLoc )
#finally, chop off the rest of file that's no longer needed
f.truncate( writeLoc )
これを試して:
currentReadPos = 0
removedLinesLength = 0
for line in file:
currentReadPos = file.tell()
if remove(line):
removedLinesLength += len(line)
else:
file.seek(file.tell() - removedLinesLength)
file.write(line + "\n")
file.flush()
file.seek(currentReadPos)
私はこれを実行していませんが、削除したい行を保持したい行で上書きして、ファイルをその場で変更するという考えです。シークと変更がファイルの反復とどのように相互作用するかはわかりません。
更新:
1GBのファイルを作成fileinput
してインプレースで試しました。私が予想していたのは、起こったこととは異なっていました。今回はドキュメントをきちんと読みました。
オプションのインプレース フィルタリング: キーワード引数 inplace=1 が fileinput.input() または FileInput コンストラクターに渡される場合、ファイルはバックアップ ファイルに移動され、標準出力は入力ファイルに送信されます (バックアップ ファイルと同じ名前が既に存在する場合、サイレントに置き換えられます)。
したがって、これは現在あなたにとって選択肢ではないようです。他の回答を確認してください。
編集前:
ファイルをinplaceで編集する場合は、Python のfileinput
モジュール - Docsを確認してください。
10GBファイルで使用した場合の効率についてはよくわかりません. しかし、私には、これが Python を使用する唯一の選択肢のように思えました。
ファイルを順番に読み書きするだけです。
f.readlines() は、ファイル内のすべてのデータ行を含むリストを返します。オプションのパラメーター sizehint を指定すると、ファイルからそのバイト数と行を完了するのに十分なバイト数を読み取り、そこから行を返します。これは、大きなファイルを行単位で効率的に読み取るためによく使用されますが、ファイル全体をメモリにロードする必要はありません。完全な行のみが返されます。
10/20 MB 以上のチャンクを取得してファイルを処理します。これが最速の方法です。
これを行う他の方法は、このファイルをストリーミングし、AWK などを使用してフィルタリングすることです。
擬似コードの例:
file = open(rw)
linesCnt=50
newReadOffset=0
tmpWrtOffset=0
rule=1
processFile()
{
while(rule)
{
(lines,newoffset)=getLines(file, newReadOffset)
if lines:
[x for line in lines if line==cool: line]
tmpWrtOffset = writeBackToFile(file, x, tmpWrtOffset) #should return new offset to write for the next time
else:
rule=0
}
}
最後にファイルのサイズを変更するには、truncate(size=None)を使用します