TypeError: 'str' unterstützt die Pufferschnittstelle nicht

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) 

Der obige Python-Code gibt mir folgenden Fehler:

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
Zukünftiger König
quelle
1
@ MikePennington: Bitte erklären Sie, warum das Komprimieren von Text nicht sinnvoll ist.
Galinette

Antworten:

295

Wenn Sie Python3x verwenden, stringist dies nicht derselbe Typ wie für Python 2.x. Sie müssen es in Bytes umwandeln (codieren).

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

Verwenden Sie auch keine Variablennamen wie stringoder filewährend dies Namen von Modulen oder Funktionen sind.

EDIT @Tom

Ja, Nicht-ASCII-Text wird ebenfalls komprimiert / dekomprimiert. Ich verwende polnische Buchstaben mit UTF-8-Codierung:

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
quelle
Es ist seltsam, dass dies das Problem behoben hat. Der ursprüngliche Code funktionierte für mich unter 3.1, und der Beispielcode in den Dokumenten wird auch nicht explizit codiert. Wenn Sie es für Nicht-ASCII-Text verwenden, dekomprimiert gunzip es? Ich habe einen Fehler bekommen.
Tom Zych
Ich habe meinen Namen in Unicode Hindi eingegeben und er wurde erfolgreich in gzip komprimiert. Ich benutze Python 3.2
Future King
@ Tom Zych: Hat wahrscheinlich etwas mit den Änderungen in 3.2 zu tun: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel
Ich habe es mit ActiveState Python 3.1 und 3.2 getestet. Auf meinem Computer funktioniert es in beiden.
Michał Niklas
1
Für Datei - Komprimierung sollten Sie immer öffnen Sie die Eingabe im Binär - Modus: Sie müssen später die Datei dekomprimieren zu können und genau den gleichen Inhalt bekommen. Das Konvertieren in Unicode ( str) und zurück ist nicht erforderlich und birgt das Risiko, dass Fehler oder Fehlanpassungen zwischen Eingabe und Ausgabe dekodiert werden.
Alexis
96

Es gibt eine einfachere Lösung für dieses Problem.

Sie müssen nur ein tzum Modus hinzufügen , damit es wird wt. Dies führt dazu, dass Python die Datei als Textdatei und nicht als Binärdatei öffnet. Dann wird einfach alles funktionieren.

Das komplette Programm lautet wie folgt:

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)
user1175849
quelle
Funktioniert es auch auf Python2? Könnte es eine Möglichkeit sein, den Code auf Python2 und Python3 zum Laufen zu bringen?
Loïc Faure-Lacroix
Wow, Mann, du bist gut! Vielen Dank! Lass mich dich abstimmen. Dies sollte die akzeptierte Antwort sein :))
Loïc
15
Das Hinzufügen von "t" kann Nebenwirkungen haben. Unter Windows-Dateien, die als Text codiert sind, werden Zeilenumbrüche ("\ n") in CRLF ("\ r \ n") konvertiert.
BitwiseMan
42

Sie können eine Python 3-Zeichenfolge nicht in Bytes serialisieren, ohne die Konvertierung in eine bestimmte Codierung zu erläutern.

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

ist möglicherweise was du willst. Dies funktioniert auch für Python 2.x und 3.x.

Andreas Jung
quelle
28

Für Python 3.x können Sie Ihren Text in Rohbytes konvertieren durch:

bytes("my data", "encoding")

Beispielsweise:

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

Das zurückgegebene Objekt funktioniert mit outfile.write.

Skurmedel
quelle
9

Dieses Problem tritt häufig beim Wechsel von py2 zu py3 auf. In PY2 plaintextist sowohl eine Zeichenkette und ein Byte - Array - Typ. In py3 plaintextist nur eine Zeichenfolge vorhanden , und die Methode outfile.write()benötigt beim Öffnen im Binärmodus tatsächlich ein Byte-Arrayoutfile , sodass eine Ausnahme ausgelöst wird . Ändern Sie die Eingabe in plaintext.encode('utf-8'), um das Problem zu beheben. Lesen Sie weiter, wenn Sie dies stört.

In py2 hat die Deklaration für file.write den Anschein erweckt , als hätten Sie eine Zeichenfolge übergeben : file.write(str). Eigentlich haben Sie ein Byte-Array übergeben, Sie sollten die Deklaration wie folgt gelesen haben : file.write(bytes). Wenn Sie es so das Problem ist einfach zu lesen, file.write(bytes)muss ein Byte - Typ und in py3 bekommen Bytes aus einem str Sie es konvertieren:

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

Warum haben die py2-Dokumente angegeben, dass file.writeeine Zeichenfolge verwendet wurde? Nun, in py2 war die Deklarationsunterscheidung nicht wichtig, weil:

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

Die str-bytes- Klasse von py2 verfügt über Methoden / Konstruktoren, mit denen sie sich in gewisser Weise wie eine String-Klasse und in anderen wie eine Byte-Array-Klasse verhält. Praktisch für file.writenicht wahr?:

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

Warum hat py3 dieses schöne System kaputt gemacht? Nun, weil in py2 grundlegende String-Funktionen für den Rest der Welt nicht funktionierten. Die Länge eines Wortes mit einem Nicht-ASCII-Zeichen messen?

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

Die ganze Zeit über Sie dachten , Sie wurden für die fragen len einer Zeichenkette in py2, Sie waren immer die Länge des Byte - Array von der Codierung. Diese Mehrdeutigkeit ist das grundlegende Problem bei Klassen mit doppelter Pflicht. Welche Version eines Methodenaufrufs implementieren Sie?

Die gute Nachricht ist dann, dass py3 dieses Problem behebt. Es entwirrt die Klassen str und bytes . Die str- Klasse verfügt über stringähnliche Methoden, die separate Byteklasse über Byte-Array-Methoden:

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

Hoffentlich hilft es, das Problem zu enträtseln und den Migrationsschmerz ein wenig leichter zu ertragen.

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

Wenn jemand eine bessere Idee hat, schlagen Sie mich bitte vor oder bearbeiten Sie mich jederzeit hier. Ich bin nur ein Neuling

Tapasit Suesasiton
quelle
Sie können s.encode('utf-8')es auch so pythonisch verwenden wie als s.decode('utf-8')Ersatz fürs = bytes("s", "utf-8")
Hans Zimermann
4

Für Djangoin django.test.TestCaseUnit - Tests, änderte ich meine Python2 Syntax:

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

So verwenden Sie die Python3- .decode('utf8') Syntax:

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