如何在Python 3中将文本流编码为字节流?

10

将字节流解码为文本流很容易:

import io
f = io.TextIOWrapper(io.BytesIO(b'Test\nTest\n'), 'utf-8')
f.readline()

在这个例子中,io.BytesIO(b'Test\nTest\n') 是一个字节流,f 是一个文本流。
我想要做的恰好相反。给定一个文本流或类似文件的对象,我想将其编码为一个字节流或文件对象而不处理整个流
到目前为止,我尝试了以下方法:
import io, codecs

f = codecs.getreader('utf-8')(io.StringIO('Test\nTest\n'))
f.readline()
# TypeError: can't concat str to bytes

f = codecs.EncodedFile(io.StringIO('Test\nTest\n'), 'utf-8')
f.readline()
# TypeError: can't concat str to bytes

f = codecs.StreamRecoder(io.StringIO('Test\nTest\n'), None, None,
                         codecs.getreader('utf-8'), codecs.getwriter('utf-8'))
# TypeError: can't concat str to bytes

f = codecs.encode(io.StringIO('Test\nTest\n'), 'utf-8')
# TypeError: utf_8_encode() argument 1 must be str, not _io.StringIO

f = io.TextIOWrapper(io.StringIO('Test\nTest\n'), 'utf-8')
f.readline()
# TypeError: underlying read() should have returned a bytes-like object, not 'str'

f = codecs.iterencode(io.StringIO('Test\nTest\n'), 'utf-8')
next(f)
# This works, but it's an iterator instead of a file-like object or stream.

f = io.BytesIO(io.StringIO('Test\nTest\n').getvalue().encode('utf-8'))
f.readline()
# This works, but I'm reading the whole stream before converting it.

我正在使用Python 3.7

1个回答

6
你可以很容易地自己编写这个功能,只需要决定如何进行缓冲。
例如:
class BytesIOWrapper(io.RawIOBase):
    def __init__(self, file, encoding='utf-8', errors='strict'):
        self.file, self.encoding, self.errors = file, encoding, errors
        self.buf = b''
    def readinto(self, buf):
        if not self.buf:
            self.buf = self.file.read(4096).encode(self.encoding, self.errors)
            if not self.buf:
                return 0
        length = min(len(buf), len(self.buf))
        buf[:length] = self.buf[:length]
        self.buf = self.buf[length:]
        return length
    def readable():
        return True

我认为这正是您所要求的内容。
>>> f = BytesIOWrapper(io.StringIO("Test\nTest\n"))
>>> f.readline()
b'Test\n'
>>> f.readline()
b'Test\n'
>>> f.readline()
b''

如果你想变得更聪明,你可能想要包装一个codecs.iterencode而不是每次缓冲4K。或者,由于我们正在使用缓冲区,您可能希望创建一个BufferedIOBase而不是RawIOBase。此外,名为BytesIOWrapper的类可能应该处理write,但那很容易。困难的部分将是实现seek/tell,因为您不能在TextIOBase内任意寻找;使寻找到开头和结尾非常容易;另一方面,在已知的先前位置进行查找则很困难(除非依赖于TextIOBase.tell返回字节位置-这并不保证做到,而且,虽然TextIOWrapper确实如此,但StringIO则不是...)。

无论如何,我认为这是编写甚至最复杂的io类的最简单演示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接