9

在 Python 中,逐行读取大文本文件很简单:

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|$)或类似的。

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

于 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 回答