【问题标题】:How to read records terminated by custom separator from file in python?如何从python中的文件中读取由自定义分隔符终止的记录?
【发布时间】:2013-11-05 04:47:10
【问题描述】:

我想要一种在 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

【问题讨论】:

    标签: python file io record separator


    【解决方案1】:

    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)
    

    【讨论】:

      【解决方案2】:

      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 分隔符,则不能……)

      【讨论】:

      • 在阅读了 OP 链接的整个跟踪器问题后,看起来 Douglas Alan 已经发布了一个非常相似的食谱 5 years into the discussion。我更喜欢他的,因为它允许您将输入换行符转换为输出换行符,而不是仅仅丢弃它……而不是编辑我的以匹配,我将留下链接。
      • 链接的另一个优点是它在流关闭时返回缓冲区的剩余部分。
      • @jozxyqk:我不确定你的意思。此版本在 EOF 处产生缓冲区的剩余部分。 (如果文件实际上已从您下方关闭并引发异常,我假设您想要该异常 - 毕竟,重点是像“for line in file:”一样工作,但使用不同的分隔符。)跨度>
      • 啊,我的错,我应该更仔细地阅读。我通过直接读取sys.stdin 并打印输出进行测试,仍然使用\n,并且由于某种原因,当我按下 ctrl-D 时剩余的字符没有打印出来。再次查看代码,我不知道为什么并假设我做错了什么。
      • @jozxyqk:从行缓冲标准输入读取与 ^D 有一些奇怪之处,这取决于您的平台、终端和 Python 版本,这可能会妨碍测试其他内容。 (看看“for line in sys.stdin():”和“for line in iter(input, ''):”是否为你做不同的事情......)
      猜你喜欢
      • 2021-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多