Python: bz2 и lzma в режиме 'wt' не пишут спецификацию (в то время как gzip делает).Зачем? - PullRequest
3 голосов
/ 14 марта 2019

Следующий код записывает сжатый текстовый файл, используя 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 (Byte Order Mark), в то время как gzip делает как положено. Это означает, что если я попытаюсь прочитать файлы bz2 / lzma в текстовом режиме (например, bz2.open(path, 'rt', encoding='utf-16')), появится UnicodeError с жалобой на отсутствующую спецификацию.

Почему это? Это ошибка?

1 Ответ

0 голосов
/ 15 марта 2019

Я отвечаю на свой вопрос.Короче говоря: да, это определенно ошибка реализации C на io.TextIOWrapper.

. Когда вы открываете файл в текстовом режиме (сжатый или нет), возвращается io.TextIOWrapper, который упаковывает двоичный файлчитатель.io.TextIOWrapper реализован на C в модуле расширения _io.Оказывается, есть также реализация Python модуля io, а именно модуля _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'
...