Pythonを使用して大きなバイナリファイルから文字シーケンスを削除する

StackOverflow https://stackoverflow.com/questions/221386

  •  03-07-2019
  •  | 
  •  

質問

Pythonのバイナリファイルから同じ値の長いシーケンスをトリムします。簡単な方法は、ファイルを読み込んで、re.subを使用して不要なシーケンスを置き換えることです。もちろん、これは大きなバイナリファイルでは機能しません。 numpyのようなものでそれを行うことができますか?

役に立ちましたか?

解決

実行するメモリがないopen("big.file").read()の場合、numpyは本当に役立ちません。Python変数と同じメモリを使用します(1GBのRAMがある場合、1GBのデータしかnumpyにロードできません)

解決策は簡単です。ファイルをチャンクで読み取ります。f = open("big.file", "rb")、一連のf.read(500)を実行し、シーケンスを削除して、別のファイルオブジェクトに書き戻します。 Cでファイルの読み取り/書き込みを行う方法はほとんどありません。

問題は、置換するパターンを見逃した場合です。例:

target_seq = "567"
input_file = "1234567890"

target_seq.read(5) # reads 12345, doesn't contain 567
target_seq.read(5) # reads 67890, doesn't contain 567

明らかな解決策は、ファイルの最初の文字から開始し、len(target_seq)文字をチェックしてから、1文字先に進み、再度先にチェックすることです。

例(擬似コード!):

while cur_data != "":
    seek_start = 0
    chunk_size = len(target_seq)

    input_file.seek(offset = seek_start, whence = 1) #whence=1 means seek from start of file (0 + offset)
    cur_data = input_file.read(chunk_size) # reads 123
    if target_seq == cur_data:
        # Found it!
        out_file.write("replacement_string")
    else:
        # not it, shove it in the new file
        out_file.write(cur_data)
    seek_start += 1

これは最も効率的な方法ではありませんが、機能します。ファイルのコピーをメモリ(または2つ)に保持する必要はありません。

他のヒント

2つのコピーがメモリに収まる場合、簡単にコピーを作成できます。 2番目のコピーは圧縮バージョンです。もちろん、numpyを使用できますが、 arrayも使用できます。 パッケージ。さらに、ビッグバイナリオブジェクトをバイト文字列として扱い、それを直接操作できます。

ファイルのサイズは本当に大きく、2つのコピーをメモリに収めることができないようです。 (多くの詳細を提供しなかったので、これは単なる推測に過ぎません。)チャンクで圧縮を行う必要があります。チャンクを読み取り、そのチャンクでいくつかの処理を行ってから書き出します。繰り返しますが、numpy、配列、またはバイトの単純な文字列は正常に動作します。

dbrの解決策は良い考えですが、次のチャンクを読む前に、ファイルポインターを検索しているシーケンスの長さに巻き戻すだけで少し複雑になります。

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
 inputFile  = open(inFilename, "rb")
 outputFile = open(outFilename, "wb")

 data = ""
 chunk = 1024

 while 1:
      data = inputFile.read(chunk)
      data = data.replace(oldSeq, newSeq)
      outputFile.write(data)

      inputFile.seek(-len(oldSequence), 1)
      outputFile.seek(-len(oldSequence), 1)

     if len(data) < chunk:
           break

 inputFile.close()
 outputFile.close()

AJMayorgaの提案は、置換文字列のサイズが異なる場合を除き問題ありません。または、置換文字列はチャンクの最後にあります。

このように修正しました:

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
    inputFile  = open(inFilename, "rb")
    outputFile = open(outFilename, "wb")

data = ""
chunk = 1024

oldSeqLen = len(oldSeq)

while 1:
    data = inputFile.read(chunk)

    dataSize = len(data)
    seekLen= dataSize - data.rfind(oldSeq) - oldSeqLen
    if seekLen > oldSeqLen:
        seekLen = oldSeqLen

    data = data.replace(oldSeq, newSeq)
    outputFile.write(data)
    inputFile.seek(-seekLen, 1) 
    outputFile.seek(-seekLen, 1)

    if dataSize < chunk:
        break

inputFile.close()
outputFile.close()

質問をより正確にする必要があります。事前にトリミングしたい値を知っていますか?

実行すると仮定すると、おそらくsubprocessを使用して一致するセクションを検索し、<!> quot; fgrep -o -b <search string> <!> quot;を実行します。 python fileオブジェクトのseekreadおよびwriteメソッドを使用して、ファイルの関連セクションを変更します。

このジェネレーターベースのバージョンは、一度に1文字のファイルコンテンツのみをメモリに保持します。

私はあなたの質問のタイトルを非常に文字通り取っていることに注意してください-あなたは同じ文字の実行を単一の文字に減らしたいです。一般にパターンを置き換える場合、これは機能しません:

import StringIO

def gen_chars(stream):
   while True:
      ch = stream.read(1)
      if ch: 
         yield ch
      else:
         break

def gen_unique_chars(stream):
   lastchar = ''
   for char in gen_chars(stream):
      if char != lastchar:
         yield char
      lastchar=char

def remove_seq(infile, outfile):
   for ch in gen_unique_chars(infile):
      outfile.write(ch)

# Represents a file open for reading
infile  = StringIO.StringIO("1122233333444555")

# Represents a file open for writing
outfile = StringIO.StringIO()

# Will print "12345"
remove_seq(infile, outfile)
outfile.seek(0)
print outfile.read()
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top