TypeError: "str" ​​non supporta l'interfaccia del 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) 

Il codice Python sopra mi sta dando il seguente errore:

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
@ MikePennington: per favore spiega perché la compressione del testo non è utile?
galinette

Risposte:


296

Se usi Python3x, allora stringnon è lo stesso tipo di Python 2.x, devi lanciarlo in byte (codificarlo).

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

Inoltre, non utilizzare nomi di variabili come stringo filementre questi sono nomi di modulo o funzione.

EDIT @Tom

Sì, anche il testo non ASCII viene compresso / decompresso. Uso lettere polacche con codifica 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)

È strano che questo l'abbia risolto; il codice originale ha funzionato per me sotto 3.1 e anche il codice di esempio nella documentazione non codifica esplicitamente. Se lo usi su testo non ASCII, gunzip lo decomprime? Ho ricevuto un errore.
Tom Zych

Ho digitato il mio nome in Unicode Hindi e l'ho compresso con successo in gzip. Sto usando Python 3.2
Future King

@Tom Zych: Probabilmente ha qualcosa a che fare con le modifiche nella 3.2: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel

L'ho testato con ActiveState Python 3.1 e 3.2. Sulla mia macchina funziona in entrambi.
Michał Niklas

1
Per la compressione dei file dovresti sempre aprire l'input in modalità binaria: devi essere in grado di decomprimere il file in un secondo momento e ottenere esattamente lo stesso contenuto. La conversione in Unicode ( str) e viceversa non è necessaria e rischia di errori di decodifica o mancate corrispondenze tra input e output.
alexis

96

C'è una soluzione più semplice a questo problema.

Hai solo bisogno di aggiungere un talla modalità in modo che diventi wt. Ciò fa sì che Python apra il file come file di testo e non binario. Allora tutto funzionerà.

Il programma completo diventa questo:

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)

Funziona anche su python2? Potrebbe essere un modo per far funzionare il codice su python2 e python3?
Loïc Faure-Lacroix

Wow, sei bravo! Grazie! Lascia che ti voti. Questa dovrebbe essere la risposta accettata :))
Loïc

15
L'aggiunta di "t" può avere effetti collaterali. Su Windows i file codificati come testo avranno le nuove righe ("\ n") convertite in CRLF ("\ r \ n").
BitwiseMan

42

Non è possibile serializzare una "stringa" di Python 3 in byte senza la conversione esplicita in qualche codifica.

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

è forse quello che vuoi. Anche questo funziona sia per python 2.x che per 3.x.


28

Per Python 3.x puoi convertire il tuo testo in byte grezzi tramite:

bytes("my data", "encoding")

Per esempio:

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

L'oggetto restituito funzionerà con outfile.write.


9

Questo problema si verifica comunemente quando si passa da py2 a py3. In py2 plaintextè sia una stringa che un tipo di array di byte . In py3 plaintextè solo una stringa e il metodo in outfile.write()realtà accetta un array di byte quando outfileviene aperto in modalità binaria, quindi viene sollevata un'eccezione. Modificare l'input in plaintext.encode('utf-8')per risolvere il problema. Continua a leggere se questo ti dà fastidio.

Nel PY2, la dichiarazione per file.write sembrava che avete passato in una stringa: file.write(str). In realtà si passavano in un array di byte, si avrebbe dovuto essere la lettura della dichiarazione in questo modo: file.write(bytes). Se lo leggi in questo modo il problema è semplice, ha file.write(bytes)bisogno di un tipo di byte e in py3 per ottenere byte da una str lo converti:

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

Perché la dichiarazione dei documenti py2 ha file.writepreso una stringa? Ebbene in py2 la distinzione della dichiarazione non aveva importanza perché:

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

La classe str-bytes di py2 ha metodi / costruttori che la fanno comportare come una classe stringa in alcuni modi e come una classe array di byte in altri. Comodo perché file.writenon è vero ?:

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

Perché py3 ha rotto questo bel sistema? Bene, perché in py2 le funzioni di base delle stringhe non funzionavano per il resto del mondo. Misurare la lunghezza di una parola con un carattere 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

Per tutto questo tempo hai pensato di chiedere la lunghezza di una stringa in py2, stavi ottenendo la lunghezza dell'array di byte dalla codifica. Quell'ambiguità è il problema fondamentale con le classi a doppio dovere. Quale versione di qualsiasi chiamata di metodo implementate?

La buona notizia quindi è che py3 risolve questo problema. Districa le classi str e bytes . La classe str ha metodi simili a stringhe, la classe byte separata ha metodi array di byte:

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

Si spera che questa conoscenza aiuti a demistificare il problema e renda il problema della migrazione un po 'più facile da sopportare.


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

Bene, se ti è utile nel caso in cui rimuovi il fastidioso carattere `` b ''. Se qualcuno ha un'idea migliore per favore suggeriscimi o sentiti libero di modificarmi in qualsiasi momento qui. Sono solo un principiante


Puoi anche usarlo s.encode('utf-8')in modo così pitonico come s.decode('utf-8')in sostituzione dis = bytes("s", "utf-8")
Hans Zimermann

4

Per Djangoin django.test.TestCaseunit test, ho cambiato la mia sintassi Python2 :

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

Per usare la sintassi Python3 .decode('utf8') :

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content.decode('utf8'))
    ...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.