7

我想要一种for line in file在 python 中执行的方法,其中行尾被重新定义为我想要的任何字符串。另一种说法是我想从文件而不是行中读取记录;我希望它和阅读线一样快速和方便。

这是python相当于设置perl的$/输入记录分隔符,或者Scanner在java中使用。这不一定必须使用for line in file(特别是,迭代器可能不是文件对象)。只是等效的东西,可以避免将太多数据读入内存。

另请参阅: 向标准 IO 堆栈添加对具有任意分隔符的读取记录的支持

4

2 回答 2

11

Python 2.xfile对象或 Python 3.3io类中没有任何内容可让您为readline. (for line in file最终使用与 相同的代码readline。)

但是自己构建它很容易。例如:

def delimited(file, delimiter='\n', bufsize=4096):
    buf = ''
    while True:
        newbuf = file.read(bufsize)
        if not newbuf:
            yield buf
            return
        buf += newbuf
        lines = buf.split(delimiter)
        for line in lines[:-1]:
            yield line
        buf = lines[-1]

这是一个愚蠢的例子:

>>> s = io.StringIO('abcZZZdefZZZghiZZZjklZZZmnoZZZpqr')
>>> d = delimited(s, 'ZZZ', bufsize=2)
>>> list(d)
['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr']

如果你想让它同时适用于二进制文件和文本文件,尤其是在 3.x 中,那就有点棘手了。但如果它只适用于一种或另一种(以及一种或另一种语言),你可以忽略它。

同样,如果您使用的是 Python 3.x(或使用ioPython 2.x 中的对象),并且想要利用已经在 a 中维护的缓冲区,BufferedIOBase而不是仅仅在缓冲区顶部放置一个缓冲区,那就是更棘手。文档确实解释了io如何做所有事情......但我不知道任何简单的例子,所以你真的必须阅读该页面的至少一半并略读其余部分。(当然,您可以直接使用原始文件……但如果您想查找 unicode 分隔符,则不能……)

于 2013-10-25T22:48:04.877 回答
0

OP 链接的问题讨论还有另一种解决方案,可以从 Alan Barnet 发布的文件中读取由自定义分隔符终止的数据行。它适用于文本和二进制文件,是fileLineIter对 Douglas Alan 方法的重大改进。

这是我对 Alan Barnet 的resplit. 我已经用据称更快的字符串连接替换了字符串添加+= "".join添加了类型提示以获得更快的性能。我的版本已调整为使用二进制文件。我必须使用正则表达式模式进行拆分,因为我的普通形式的定界符也出现在非定界函数的数据行内,所以我需要考虑它的上下文。str但是,如果您有一个在其他地方没有使用的简单且唯一的分隔符,您可以为文本文件重新调整它并用 common 替换正则表达式模式。

import pathlib
import functools
import re
from typing import Iterator, Iterable, ByteString
import logging

logging.basicConfig(level=logging.DEBUG)
logging.getLogger().setLevel(logging.DEBUG)
logger = logging.getLogger(__name__)


def resplit(chunks_of_a_file: Iterator, split_pattern: re.Pattern) -> Iterable[ByteString]:
    """
    Reads chunks of a file one chunk at a time, 
    splits them into data rows by `split_pattern` 
    and joins partial data rows across chunk boundaries.
    borrowed from https://bugs.python.org/issue1152248#msg223491
    """
    partial_line = None
    for chunk in chunks_of_a_file:
        if partial_line:
            partial_line = b"".join((partial_line, chunk))
        else:
            partial_line = chunk
        if not chunk:
            break
        lines = split_pattern.split(partial_line)
        partial_line = lines.pop()
        yield from lines
    if partial_line:
        yield partial_line


if __name__ == "__main__":
    path_to_source_file = pathlib.Path("source.bin")
    with open(path_to_source_file, mode="rb") as file_descriptor:
        buffer_size = 8192
        sentinel = b""
        chunks = iter(functools.partial(file_descriptor.read, buffer_size), sentinel)
        data_rows_delimiter = re.compile(b"ABC")
        lines = resplit(chunks, data_rows_delimiter)
        for line in lines:
            logger.debug(line)
于 2019-09-18T02:47:05.327 回答