Python:模式 'wt' 中的 bz2 和 lzma 不写 BOM(而 gzip 会写)。为什么?
Python: bz2 and lzma in mode 'wt' don't write the BOM (while gzip does). Why?
以下代码使用 gzip、bz2 和 lzma 写入压缩文本文件,然后读取并打印其二进制内容。
import bz2
import gzip
import lzma
import os
def test(encoding):
print(encoding)
for module in [gzip, bz2, lzma]:
path = '/tmp/test.txt.%s' % module.__name__
if os.path.exists(path):
os.remove(path)
with module.open(path, 'wt', encoding=encoding) as fout:
fout.write('Ciao')
with module.open(path, 'rb') as fin:
print("%8s" % module.__name__, 'bytes:', fin.read())
test('utf-16')
print('')
test('utf-32')
输出为:
utf-16
gzip bytes: b'\xff\xfeC\x00i\x00a\x00o\x00'
bz2 bytes: b'C\x00i\x00a\x00o\x00'
lzma bytes: b'C\x00i\x00a\x00o\x00'
utf-32
gzip bytes: b'\xff\xfe\x00\x00C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
bz2 bytes: b'C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
lzma bytes: b'C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
如您所见,bz2 和 lzma 不写 BOM(字节顺序标记),而 gzip 按预期写。这意味着如果我尝试以文本模式(例如 bz2.open(path, 'rt', encoding='utf-16')
)读取 bz2/lzma 文件,则会引发 UnicodeError
抱怨缺少 BOM。
这是为什么?这是一个错误吗?
我正在回答我自己的问题。简而言之:是的,这绝对是 io.TextIOWrapper
.
的 C 实现的错误
当您以文本模式(压缩或非压缩)打开文件时,返回的是 io.TextIOWrapper
包装二进制文件 reader。 io.TextIOWrapper
在 _io
扩展模块中用 C 实现。原来io
模块也有一个Python实现,即_pyio
模块。 _pyio.TextIOWrapper
工作正常,所以这绝对是 C 实现的一个错误。
以下代码演示了该问题:
import bz2
import io
import _pyio
def test(io_module, encoding='utf-16'):
path = '/tmp/test.txt.bz2'
with io_module.TextIOWrapper(bz2.open(path, 'w'), encoding=encoding) as fout:
fout.write('Ciao')
with bz2.open(path, 'rb') as fin:
print("%5s" % io_module.__name__, 'bytes:', fin.read())
test(io)
test(_pyio)
打印:
io bytes: b'C\x00i\x00a\x00o\x00'
_pyio bytes: b'\xff\xfeC\x00i\x00a\x00o\x00'
以下代码使用 gzip、bz2 和 lzma 写入压缩文本文件,然后读取并打印其二进制内容。
import bz2
import gzip
import lzma
import os
def test(encoding):
print(encoding)
for module in [gzip, bz2, lzma]:
path = '/tmp/test.txt.%s' % module.__name__
if os.path.exists(path):
os.remove(path)
with module.open(path, 'wt', encoding=encoding) as fout:
fout.write('Ciao')
with module.open(path, 'rb') as fin:
print("%8s" % module.__name__, 'bytes:', fin.read())
test('utf-16')
print('')
test('utf-32')
输出为:
utf-16
gzip bytes: b'\xff\xfeC\x00i\x00a\x00o\x00'
bz2 bytes: b'C\x00i\x00a\x00o\x00'
lzma bytes: b'C\x00i\x00a\x00o\x00'
utf-32
gzip bytes: b'\xff\xfe\x00\x00C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
bz2 bytes: b'C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
lzma bytes: b'C\x00\x00\x00i\x00\x00\x00a\x00\x00\x00o\x00\x00\x00'
如您所见,bz2 和 lzma 不写 BOM(字节顺序标记),而 gzip 按预期写。这意味着如果我尝试以文本模式(例如 bz2.open(path, 'rt', encoding='utf-16')
)读取 bz2/lzma 文件,则会引发 UnicodeError
抱怨缺少 BOM。
这是为什么?这是一个错误吗?
我正在回答我自己的问题。简而言之:是的,这绝对是 io.TextIOWrapper
.
当您以文本模式(压缩或非压缩)打开文件时,返回的是 io.TextIOWrapper
包装二进制文件 reader。 io.TextIOWrapper
在 _io
扩展模块中用 C 实现。原来io
模块也有一个Python实现,即_pyio
模块。 _pyio.TextIOWrapper
工作正常,所以这绝对是 C 实现的一个错误。
以下代码演示了该问题:
import bz2
import io
import _pyio
def test(io_module, encoding='utf-16'):
path = '/tmp/test.txt.bz2'
with io_module.TextIOWrapper(bz2.open(path, 'w'), encoding=encoding) as fout:
fout.write('Ciao')
with bz2.open(path, 'rb') as fin:
print("%5s" % io_module.__name__, 'bytes:', fin.read())
test(io)
test(_pyio)
打印:
io bytes: b'C\x00i\x00a\x00o\x00'
_pyio bytes: b'\xff\xfeC\x00i\x00a\x00o\x00'