我想在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
答案 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)