TypeError: 'str' no admite la interfaz del búfer

Resuelto Future King asked hace 13 años • 7 respuestas
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) 

El código Python anterior me da el siguiente error:

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
Future King avatar Mar 29 '11 17:03 Future King
Aceptado

Si usa Python3x, entonces stringno es del mismo tipo que Python 2.x, debe convertirlo en bytes (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'))

Tampoco utilice nombres de variables como stringo filemientras sean nombres de módulo o función.

EDITAR @Tom

Sí, el texto que no es ASCII también se comprime/descomprime. Utilizo letras polacas con codificación 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 avatar Mar 29 '2011 10:03 Michał Niklas

Hay una solución más sencilla a este problema.

Sólo necesitas agregar un tal modo para que se convierta en wt. Esto hace que Python abra el archivo como un archivo de texto y no como binario. Entonces todo funcionará.

El programa completo queda así:

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 avatar Jul 21 '2014 08:07 user1175849

No se puede serializar una 'cadena' de Python 3 en bytes sin una conversión explícita a alguna codificación.

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

es posiblemente lo que quieres. Además, esto funciona tanto para Python 2.xy 3.x.

 avatar Mar 29 '2011 10:03

Para Python 3.x puedes convertir tu texto a bytes sin formato a través de:

bytes("my data", "encoding")

Por ejemplo:

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

El objeto devuelto funcionará con outfile.write.

Skurmedel avatar Mar 29 '2011 10:03 Skurmedel

Este problema ocurre comúnmente al cambiar de py2 a py3. En py2 plaintextrepresentaba tanto un tipo de cadena como un tipo de matriz de bytes , era de tipo flexible y podía oscilar en ambos sentidos. En py3 ahora plaintextsolo hay una cadena , es más definido y el método outfile.write()en realidad toma una matriz de bytes cuando outfilese abre en modo binario, por lo que se genera una excepción. Cambie la entrada a plaintext.encode('utf-8')para solucionar el problema. Sigue leyendo si esto te molesta.

En py2, la declaración de file.write hacía que pareciera que había pasado una cadena: file.write(str). En realidad, estabas pasando una matriz de bytes, deberías haber leído la declaración de esta manera: file.write(bytes). Si lo lees así el problema es simple, file.write(bytes)necesita un tipo de bytes y en py3 para sacar bytes de un str lo conviertes:

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

¿Por qué los documentos de py2 declararon que file.writetomaron una cadena? Bueno, en py2 la distinción de declaración no importaba porque:

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

La clase str-bytes de py2 tiene métodos/constructores que la hacen comportarse como una clase de cadena en algunos aspectos y como una clase de matriz de bytes en otros. Conveniente para file.write¿no es así?:

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

¿Por qué py3 rompió este bonito sistema? Bueno, porque en py2 las funciones básicas de cadena no funcionaron para el resto del mundo. ¿Medir la longitud de una palabra con un carácter no 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

Todo este tiempo pensaste que estabas pidiendo la longitud de una cadena en py2, estabas obteniendo la longitud de la matriz de bytes de la codificación. Esa ambigüedad es el problema fundamental de las clases con doble función. ¿Qué versión de cualquier llamada a método implementas?

Entonces, la buena noticia es que py3 soluciona este problema. Desenreda las clases str y bytes . La clase str tiene métodos similares a cadenas, la clase de bytes separados tiene métodos de matriz de bytes:

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

Esperemos que saber esto ayude a desmitificar el problema y haga que el dolor de la migración sea un poco más fácil de soportar.

Rian Rizvi avatar Jan 09 '2016 00:01 Rian Rizvi