TypeError: 'str' no admite la interfaz del búfer
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
Si usa Python3x, entonces string
no 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 string
o file
mientras 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)
Hay una solución más sencilla a este problema.
Sólo necesitas agregar un t
al 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)
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.
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
.
Este problema ocurre comúnmente al cambiar de py2 a py3. En py2 plaintext
representaba 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 plaintext
solo hay una cadena , es más definido y el método outfile.write()
en realidad toma una matriz de bytes cuando outfile
se 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.write
tomaron 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.