TypeError: 'str' не поддерживает интерфейс буфера
plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wb") as outfile:
outfile.write(plaintext)
Приведенный выше код python дает мне следующую ошибку:
Traceback (most recent call last):
File "C:/Users/Ankur Gupta/Desktop/Python_works/gzip_work1.py", line 33, in <module>
compress_string()
File "C:/Users/Ankur Gupta/Desktop/Python_works/gzip_work1.py", line 15, in compress_string
outfile.write(plaintext)
File "C:\Python32\lib\gzip.py", line 312, in write
self.crc = zlib.crc32(data, self.crc) & 0xffffffff
TypeError: 'str' does not support the buffer interface
Ответы
Ответ 1
Если вы используете Python3x, то string
не является тем же типом, что и для Python 2.x, вы должны передать его в байты (закодировать его).
plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wb") as outfile:
outfile.write(bytes(plaintext, 'UTF-8'))
Также не используйте имена переменных, такие как string
или file
, в то время как это имена модуля или функции.
EDIT @Tom
Да, текст без ASCII также сжат/распакован. Я использую польские буквы с кодировкой UTF-8:
plaintext = 'Polish text: ąćęłńóśźżĄĆĘŁŃÓŚŹŻ'
filename = 'foo.gz'
with gzip.open(filename, 'wb') as outfile:
outfile.write(bytes(plaintext, 'UTF-8'))
with gzip.open(filename, 'r') as infile:
outfile_content = infile.read().decode('UTF-8')
print(outfile_content)
Ответ 2
Существует более легкое решение этой проблемы.
Вам просто нужно добавить t
в режим, чтобы он стал wt
. Это заставляет Python открывать файл как текстовый файл, а не двоичный. Тогда все будет работать.
Полная программа станет следующей:
plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wt") as outfile:
outfile.write(plaintext)
Ответ 3
Вы не можете сериализовать строку "Python 3" в байтах без эксплицитного преобразования в некоторую кодировку.
outfile.write(plaintext.encode('utf-8'))
возможно, что вы хотите. Также это работает как для python 2.x, так и для 3.x.
Ответ 4
Для Python 3.x вы можете преобразовать текст в необработанные байты через:
bytes("my data", "encoding")
Например:
bytes("attack at dawn", "utf-8")
Возвращенный объект будет работать с outfile.write
.
Ответ 5
Эта проблема обычно возникает при переключении с py2 на py3. В py2 plaintext
- это как строка, так и тип байтового массива. В py3 plaintext
есть только строка, а метод outfile.write()
фактически принимает байтовый массив, когда outfile
открывается в двоичном режиме, поэтому возникает исключение. Измените ввод на plaintext.encode('utf-8')
, чтобы устранить проблему. Читайте дальше, если это вас беспокоит.
В py2 объявление для файла file.write показало, что вы передали строку: file.write(str)
. На самом деле вы проходили в байтовом массиве, вы должны были читать объявление следующим образом: file.write(bytes)
. Если вы читаете это так, проблема проста, file.write(bytes)
нужен тип байтов, а в py3 - для получения байтов из str, которые вы его преобразуете:
py3>> outfile.write(plaintext.encode('utf-8'))
Почему декларация py2 docs file.write
взяла строку? Ну, в py2 различие в декларации не имело значения, потому что:
py2>> str==bytes #str and bytes aliased a single hybrid class in py2
True
В классе str-bytes py2 есть методы/конструкторы, которые в некотором роде ведут себя как строковый класс и класс байтового массива в других. Удобно для file.write
, не так ли?:
py2>> plaintext='my string literal'
py2>> type(plaintext)
str #is it a string or is it a byte array? it both!
py2>> outfile.write(plaintext) #can use plaintext as a byte array
Почему py3 нарушил эту приятную систему? Хорошо, потому что в py2 основные функции строки не работали для остального мира. Измерять длину слова с помощью символа, отличного от ASCII?
py2>> len('¡no') #length of string=3, length of UTF-8 byte array=4, since with variable len encoding the non-ASCII chars = 2-6 bytes
4 #always gives bytes.len not str.len
Все это время, когда вы думали, что вы запрашиваете len строки в py2, вы получаете длину байтового массива из кодировки. Эта двусмысленность является фундаментальной проблемой для классов с двойной нагрузкой. Какую версию любого вызова метода вы реализуете?
Хорошей новостью является то, что py3 исправляет эту проблему. Он распутывает классы str и bytes. Класс str имеет строковые методы, отдельный класс байтов имеет методы массива байтов:
py3>> len('¡ok') #string
3
py3>> len('¡ok'.encode('utf-8')) #bytes
4
Мы надеемся, что это поможет устранить проблему, и облегчить перенос миграции.
Ответ 6
>>> s = bytes("s","utf-8")
>>> print(s)
b's'
>>> s = s.decode("utf-8")
>>> print(s)
s
Хорошо, если вам полезно в случае удаления раздражающего символа "b". Если кто-нибудь получил лучшую идею, пожалуйста, предложите мне или не стесняйтесь редактировать меня в любое время здесь. Я просто новичок.
Ответ 7
Для Django
в django.test.TestCase
модульном тестировании я изменил синтаксис Python2:
def test_view(self):
response = self.client.get(reverse('myview'))
self.assertIn(str(self.obj.id), response.content)
...
Чтобы использовать синтаксис Python3 .decode('utf8')
:
def test_view(self):
response = self.client.get(reverse('myview'))
self.assertIn(str(self.obj.id), response.content.decode('utf8'))
...