如何读取一个大的二进制文件并通过一些标记分割其内容

时间:2013-09-15 06:26:08

标签: python

在Python中,逐行阅读大型文本文件非常简单:

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

但是如何读取二进制文件并通过某个给定标记“拆分”(通过生成器)其内容,而不是换行符'\ n'?

我想要这样的东西:

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

但是,当然,内存效率高(文件大约70GB)。当然,我们无法通过每个字节读取文件(由于硬盘性质,它会太慢)。

“块”长度(这些标记之间的数据)可能不同,理论上从1字节到兆字节。

所以,举一个总结的例子,数据看起来像这样(这里的数字是指字节,数据是二进制格式):

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

有没有简单的方法可以做到这一点(没有实现读取块,拆分块,记住尾部等)?

4 个答案:

答案 0 :(得分:5)

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 ...,可以进一步减少此函数的内存使用量。这留给读者作为练习。

答案 1 :(得分: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|$)或类似的内容。

然后,由操作系统提供访问该文件的必要条件。

答案 2 :(得分: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')): ...

确保每条“线”都不会占用太多内存......

答案 3 :(得分:0)

Readline本身读取块,分割块,记住尾巴等等。所以,不。