使用io.*类的缓冲迭代器

2024-10-05 13:22:20 发布

您现在位置:Python中文网/ 问答频道 /正文

在如下设置中写入文件时,production.iterlines()会生成许多行文本:

with open('output.txt', 'w', encoding='UTF-8') as f:
    for line in production.iterlines():
        f.write(line)

然后-如果我理解正确-会发生以下情况: TextIOWrapper对象f逐行接受并对其进行编码,并且,每当达到某个限制(例如缓冲区中的4096个字节)时,就会将一个字节块写入磁盘。因此,实际的磁盘写入操作与生成的行的速度不同,但通常不太频繁(除非行很长)。在

现在我想在这些块上有一个迭代器,例如通过网络发送它们。它可以这样使用:

^{pr2}$

因此,这个假设的buffereditor对象将参数中的字符串组合成大小合理的块,并将它们作为编码字节提供给调用方。在

我觉得通过巧妙地组合io模块中的类,这应该是可能的——我只是不知道如何使用它们。类似于StringIO,但具有某种队列行为,具有交错的写入和读取调用。在

但也许我错了,他们不是为这种任务而设计的。(毕竟,在达到固定的块大小之前,拼凑一个对字符串进行编码和连接的迭代器并不是那么难的事情。)我们鼓励您给出答案,让我更好地理解io类的用法。我通常非常喜欢官方文档,但是io模块上的文档却让我失望地落在后面。在


Tags: 模块文件对象字符串文档io文本编码
1条回答
网友
1楼 · 发布于 2024-10-05 13:22:20

这里不需要使用任何类。实际上,您并不是在执行I/O,而是在进行缓冲(这不是I/O所独有的)。尝试重用BufferIOBase接口在这里是次优的,因为您的输入是iterable,输出也是iterable;两者都不是类似于文件的对象,.read()也没有{}可用于填充缓冲区。在

您可以自己实现该类;我使用了^{} type使其尽可能高效:

from collections import deque

class BufferedIterator:
    """Turns an iterable of str lines into chunks of a fixed size"""
    def __init__(self, it, buffer=2**12):
        self._it = iter(it)
        self._buffersize = buffer
        self._lengths, self._lines = deque(), deque()

    def __iter__(self):
        return self

    def __next__(self):
        if self._it is None:
            raise StopIteration

        # collect enough lines to fill the buffer
        bsize = self._buffersize
        while sum(self._lengths) < bsize:
            line = next(self._it, None)
            if line is None:
                # out of data, build the chunk from the remainder
                # clear the iterable to flag that we are done
                self._it = None
                if not self._lines:
                    # edgecase, never had any lines to begin with
                    raise StopIteration
                break
            self._lines.append(line)
            self._lengths.append(len(line))

        # build a chunk from the buffered lines
        chunklength = 0
        chunk = []
        while self._lines and chunklength < bsize:
            length, linechunk = self._lengths.popleft(), self._lines.popleft()
            needed = bsize - chunklength
            if length > needed:
                # only need a substring
                self._lengths.appendleft(length - needed)
                self._lines.appendleft(linechunk[needed:])
                length, linechunk = needed, linechunk[:needed]
            chunklength += length
            chunk.append(linechunk)
        return ''.join(chunk)

    next = __next__  # Python 2

通过使用两个deque对象,一个用于长度,一个用于行本身,我们避免了过于频繁地调用len()。获取一个字符串的长度是足够便宜的,但是每次推送和弹出当前堆栈帧来检索它的成本很高,因此需要避免这种情况。在

演示:

^{pr2}$

相关问题 更多 >

    热门问题