python - TypeError: ''str'' no admite la interfaz de búfer
string gzip (7)
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 de python anterior me está dando 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
Este problema ocurre comúnmente cuando se cambia de py2 a py3. En py2, el plaintext
es tanto una cadena como un tipo de matriz de bytes . En py3, el plaintext
simple es solo una cadena , y el método outfile.write()
realidad toma una matriz de bytes cuando se abre outfile
en modo binario, por lo que se produce 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 hizo que pareciera que file.write(str)
una cadena: file.write(str)
. En realidad, estaba pasando una matriz de bytes, debería haber estado leyendo una declaración como esta: file.write(bytes)
. Si lo lees así, el problema es simple, file.write(bytes)
necesita un tipo de bytes y en py3 para obtener bytes de un str lo conviertes:
py3>> outfile.write(plaintext.encode(''utf-8''))
¿Por qué los documentos file.write
declaran file.write
tomó una cadena? Bueno, en py2 la distinción de declaración no importó 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 hacen que se comporte como una clase de cadena en algunos aspectos y 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 funcionaban 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 que pensó que estaba pidiendo el len de una cadena en py2, estaba obteniendo la longitud de la matriz de bytes de la codificación. Esa ambigüedad es el problema fundamental de las clases de doble función. ¿Qué versión de cualquier método de llamada implementas?
La buena noticia es que py3 soluciona este problema. Desenreda las clases str y bytes . La clase str tiene métodos parecidos a una cadena, la clase de bytes separada 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.
Hay una solución más fácil a este problema.
Solo necesitas agregar una 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 se convierte en este:
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 puede serializar una ''cadena'' de Python 3 a bytes sin una conversión explícita a alguna codificación.
outfile.write(plaintext.encode(''utf-8''))
es posiblemente lo que quieres. También esto funciona tanto para python 2.xy 3.x.
Para Django
en la django.test.TestCase
unidad django.test.TestCase
, cambié mi sintaxis de Python2 :
def test_view(self):
response = self.client.get(reverse(''myview''))
self.assertIn(str(self.obj.id), response.content)
...
Para usar la sintaxis de Python3 .decode(''utf8'')
:
def test_view(self):
response = self.client.get(reverse(''myview''))
self.assertIn(str(self.obj.id), response.content.decode(''utf8''))
...
Para Python 3.x puedes convertir tu texto a bytes en bruto a través de:
bytes("my data", "encoding")
Por ejemplo:
bytes("attack at dawn", "utf-8")
El objeto devuelto funcionará con outfile.write
.
Si usa Python3x, entonces la string
no es del mismo tipo que para Python 2.x, debe convertirla en bytes (codificarla).
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
que son nombres de módulo o función.
EDITAR @Tom
Sí, el texto que no es ASCII también se comprime / descomprime. Yo uso 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)
>>> s = bytes("s","utf-8")
>>> print(s)
b''s''
>>> s = s.decode("utf-8")
>>> print(s)
s
Bueno, si es útil para usted en caso de eliminar el molesto carácter ''b''. Si alguien tiene una idea mejor, por favor sugiérame o siéntase libre de editarme en cualquier momento aquí. Solo soy un novato.