我想要一种for line in file
在 python 中执行的方法,其中行尾被重新定义为我想要的任何字符串。另一种说法是我想从文件而不是行中读取记录;我希望它和阅读线一样快速和方便。
这是python相当于设置perl的$/
输入记录分隔符,或者Scanner
在java中使用。这不一定必须使用for line in file
(特别是,迭代器可能不是文件对象)。只是等效的东西,可以避免将太多数据读入内存。
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(或使用io
Python 2.x 中的对象),并且想要利用已经在 a 中维护的缓冲区,BufferedIOBase
而不是仅仅在缓冲区顶部放置一个缓冲区,那就是更棘手。文档确实解释了io
如何做所有事情......但我不知道任何简单的例子,所以你真的必须阅读该页面的至少一半并略读其余部分。(当然,您可以直接使用原始文件……但如果您想查找 unicode 分隔符,则不能……)
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)