TypeError: 'str' tidak mendukung antarmuka buffer

267
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) 

Kode python di atas memberi saya kesalahan berikut:

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
Raja masa depan
sumber
1
@ MikePennington: tolong jelaskan mengapa mengompresi teks tidak berguna?
galinette

Jawaban:

295

Jika Anda menggunakan Python3x maka stringbukan jenis yang sama dengan Python 2.x, Anda harus membuangnya ke byte (menyandikannya).

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'))

Juga tidak menggunakan nama variabel seperti stringatau filesementara itu adalah nama modul atau fungsi.

EDIT @Tom

Ya, teks non-ASCII juga dikompresi / didekompresi. Saya menggunakan huruf Polandia dengan pengkodean 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)
Michał Niklas
sumber
Aneh bahwa ini memperbaikinya; kode asli bekerja untuk saya di bawah 3.1, dan kode sampel dalam dokumen juga tidak menyandikan secara eksplisit. Jika Anda menggunakannya pada teks non-ASCII, apakah gunzip mendekompresnya? Saya mendapat kesalahan.
Tom Zych
Saya mengetik Nama saya di Unicode Hindi dan berhasil memampatkannya di gzip. Saya menggunakan Python 3.2
Future King
@ Tom Zych: Mungkin ada hubungannya dengan perubahan di 3.2: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel
Saya mengujinya dengan ActiveState Python 3.1 dan 3.2. Di mesin saya itu bekerja di keduanya.
Michał Niklas
1
Untuk kompresi file, Anda harus selalu membuka input dalam mode biner: Anda harus dapat mengompres file nanti dan mendapatkan konten yang persis sama. Konversi ke Unicode ( str) dan kembali tidak diperlukan, dan berisiko kesalahan penguraian atau ketidaksesuaian antara input dan output.
alexis
96

Ada solusi yang lebih mudah untuk masalah ini.

Anda hanya perlu menambahkan tke mode sehingga menjadi wt. Ini menyebabkan Python untuk membuka file sebagai file teks dan bukan biner. Maka semuanya akan bekerja.

Program lengkap menjadi ini:

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)
pengguna1175849
sumber
Apakah ini berfungsi pada python2 juga? Mungkinkah ini merupakan cara untuk membuat kode berfungsi pada python2 dan python3?
Loïc Faure-Lacroix
Wow, kau baik-baik saja! Terima kasih! Biarkan saya memilih Anda. Ini harus menjadi jawaban yang diterima :))
Loïc
15
Menambahkan "t" dapat memiliki efek samping. Pada windows file yang disandikan sebagai teks akan memiliki baris baru ("\ n") dikonversi menjadi CRLF ("\ r \ n").
BitwiseMan
42

Anda tidak dapat membuat serial 'string' Python ke byte tanpa menjelaskan konversi ke beberapa pengkodean.

outfile.write(plaintext.encode('utf-8'))

mungkin apa yang Anda inginkan. Ini juga berfungsi untuk kedua python 2.x dan 3.x.

Andreas Jung
sumber
28

Untuk Python 3.x Anda dapat mengubah teks Anda menjadi byte mentah melalui:

bytes("my data", "encoding")

Sebagai contoh:

bytes("attack at dawn", "utf-8")

Objek yang dikembalikan akan bekerja dengannya outfile.write.

Skurmedel
sumber
9

Masalah ini umumnya terjadi ketika beralih dari py2 ke py3. Dalam py2 plaintextadalah tipe string dan byte byte . Dalam py3 plaintexthanya sebuah string , dan metode ini outfile.write()benar-benar mengambil array byte saat outfiledibuka dalam mode biner, jadi pengecualian dimunculkan. Ubah input keplaintext.encode('utf-8') untuk memperbaiki masalah. Baca terus jika ini mengganggu Anda.

Dalam py2, yang deklarasi file.write membuatnya tampak seperti Anda melewati dalam sebuah string: file.write(str). Sebenarnya Anda lewat di array byte, Anda seharusnya membaca deklarasi seperti ini: file.write(bytes). Jika Anda membacanya seperti ini masalahnya sederhana, file.write(bytes)perlu tipe byte dan di py3 untuk mendapatkan byte dari str Anda mengubahnya:

py3>> outfile.write(plaintext.encode('utf-8'))

Mengapa dokumen py2 menyatakan file.writemengambil string? Nah di py2 perbedaan deklarasi tidak masalah karena:

py2>> str==bytes         #str and bytes aliased a single hybrid class in py2
True

Kelas str-bytes py2 memiliki metode / konstruktor yang membuatnya berperilaku seperti kelas string dalam beberapa hal dan kelas array byte di orang lain. Nyaman file.writebukan ?:

py2>> plaintext='my string literal'
py2>> type(plaintext)
str                              #is it a string or is it a byte array? it's both!

py2>> outfile.write(plaintext)   #can use plaintext as a byte array

Mengapa py3 merusak sistem yang bagus ini? Nah karena dalam fungsi string dasar py2 tidak bekerja untuk seluruh dunia. Mengukur panjang kata dengan karakter non-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

Selama ini Anda mengira Anda meminta len dari string di py2, Anda mendapatkan panjang byte array dari pengkodean. Ambiguitas itu adalah masalah mendasar dengan kelas tugas ganda. Versi panggilan metode apa saja yang Anda terapkan?

Kabar baiknya adalah bahwa py3 memperbaiki masalah ini. Ini menguraikan kelas str dan byte . Kelas str memiliki metode seperti string, kelas byte yang terpisah memiliki metode array byte:

py3>> len('¡ok')       #string
3
py3>> len('¡ok'.encode('utf-8'))     #bytes
4

Semoga mengetahui hal ini membantu menghilangkan misteri masalah, dan membuat rasa sakit migrasi sedikit lebih mudah untuk ditanggung.

Riaz Rizvi
sumber
4
>>> s = bytes("s","utf-8")
>>> print(s)
b's'
>>> s = s.decode("utf-8")
>>> print(s)
s

Nah, jika berguna untuk Anda jika Anda menghilangkan karakter 'b' yang mengganggu.

Tapasit Suesasiton
sumber
Anda juga dapat menggunakannya s.encode('utf-8')sangat pythonic sebagai s.decode('utf-8')penggantis = bytes("s", "utf-8")
Hans Zimermann
4

Untuk Djangodalam django.test.TestCasepengujian unit, saya mengubah sintaks Python2 saya :

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content)
    ...

Untuk menggunakan sintaks Python3 .decode('utf8') :

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content.decode('utf8'))
    ...
Aaron Lelevier
sumber