9

Python では、大きなテキストファイルを 1 行ずつ読み取るのは簡単です。

for line in open('somefile', 'r'): ...

しかし、バイナリファイルを読み取り、改行「\ n」ではなく、特定のマーカーでそのコンテンツを(ジェネレーターによって)「分割」する方法は?

私はそのようなものが欲しい:

content = open('somefile', 'r').read()
result = content.split('some_marker')

もちろん、メモリ効率が良いです (ファイルは約 70GB です)。もちろん、ファイルをすべてのバイト単位で読み取ることはできません (HDD の性質上、非常に遅くなります)。

「チャンク」の長さ (これらのマーカー間のデータ) は、理論的には 1 バイトからメガバイトまで異なる場合があります。

したがって、要約する例を示すと、データは次のようになります (ここでは数字はバイトを意味し、データはバイナリ形式です)。

12345223-MARKER-3492-MARKER-34834983428623762374632784-MARKER-888-MARKER-...

それを行う簡単な方法はありますか(チャンクでの読み取りを実装しない、チャンクを分割する、テールを記憶するなど)?

4

4 に答える 4

6

Python にはそれを行う魔法はありませんが、書くのは難しくありません。例えば:

def split_file(fp, marker):
    BLOCKSIZE = 4096
    result = []
    current = ''
    for block in iter(lambda: fp.read(BLOCKSIZE), ''):
        current += block
        while 1:
            markerpos = current.find(marker)
            if markerpos == -1:
                break
            result.append(current[:markerpos])
            current = current[markerpos + len(marker):]
    result.append(current)
    return result

この関数のメモリ使用量は、ジェネレータに変換する、つまり に変換することでさらに削減できresult.append(...)ますyield ...。これは、読者への演習として残されています。

于 2013-09-15T06:51:24.580 に答える
2

一般的なアイデアは、mmapあなたを使用して、それre.finditerを上書きすることです:

import mmap
import re

with open('somefile', 'rb') as fin:
  mf = mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ)
  markers = re.finditer('(.*?)MARKER', mf)
  for marker in markers:
    print marker.group(1)

私はテストしていませんが(.*?)(MARKER|$)、そこにまたは類似のものも必要になる場合があります。

次に、ファイルへのアクセスに必要なものを提供するのは OS の役割です。

于 2013-09-15T12:20:17.563 に答える
1

そのための組み込み関数はないと思いますが、@ user4815162342 の提案と同様に、メモリの非効率性を防ぐために、イテレータを使用してうまく「チャンクを読み込む」ことができます。

def split_by_marker(f, marker = "-MARKER-", block_size = 4096):
    current = ''
    while True:
        block = f.read(block_size)
        if not block: # end-of-file
            yield current
            return
        current += block
        while True:
            markerpos = current.find(marker)
            if markerpos < 0:
                break
            yield current[:markerpos]
            current = current[markerpos + len(marker):]

この方法では、すべての結果を一度にメモリに保存することはありませんが、次のように繰り返すことができます。

for line in split_by_marker(open(filename, 'rb')): ...

各「行」がメモリを使いすぎないようにしてください...

于 2013-09-15T07:33:14.743 に答える
0

readline 自体は、チャンクで読み取り、チャンクを分割し、末尾を記憶します。

于 2013-09-15T06:30:36.330 に答える