如何在python中从文件中读取自定义分隔符终止的记录?

时间:2013-10-25 22:39:52

标签: python file io record separator

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

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

另见: Add support for reading records with arbitrary separators to the standard IO stack

2 个答案:

答案 0 :(得分:10)

Python 2.x file对象或Python 3.3 io类中没有任何内容允许您为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(或在Python 2.x中使用io个对象),并且想要使用已在BufferedIOBase中维护的缓冲区而不只是在缓冲区顶部放一个缓冲区,这更棘手。 io文档确实解释了如何做所有事情......但我不知道任何简单的例子,所以你真的必须阅读该页面的至少一半并浏览其余部分。 (当然,你可以直接使用原始文件......但如果你想找到unicode分隔符则不行......)

答案 1 :(得分:0)

链接到问题讨论OP的yet another solution可以从Alan Barnet发布的文件中读取由自定义分隔符终止的数据行。它适用于文本文件和二进制文件,是对道格拉斯·艾伦(Douglas Alan)的fileLineIter食谱的重大改进。

这是我艾伦·巴内特(Alan Barnet)的resplit的优美版本。我已经用allegedly faster +=字符串串联替换了字符串添加"".join,并添加了类型提示以提高性能。我的版本已调整为可处理二进制文件。我必须使用正则表达式模式进行拆分,因为我的定界符也以普通形式出现在非定界函数的数据行内,因此我需要考虑其上下文。但是,如果您有一个简单独特的定界符未在其他地方使用,则可以将其重新调整为文本文件,并用普通的str替换正则表达式。

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)