TypeError: 'str' ne prend pas en charge l'interface de tampon

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) 

Le code python ci-dessus me donne l'erreur suivante:

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
la source
1
@MikePennington: veuillez expliquer pourquoi la compression de texte n'est pas utile?
galinette

Réponses:

295

Si vous utilisez Python3x alors qu'il stringn'est pas du même type que pour Python 2.x, vous devez le convertir en octets (le coder).

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

N'utilisez pas non plus de noms de variables comme stringou filealors que ce sont des noms de module ou de fonction.

EDIT @Tom

Oui, le texte non ASCII est également compressé / décompressé. J'utilise des lettres polonaises avec un encodage 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
la source
C'est étrange que cela le fixe; le code d'origine fonctionnait pour moi sous 3.1, et l'exemple de code dans les documents ne codait pas non plus explicitement. Si vous l'utilisez sur du texte non ASCII, gunzip le décompresse-t-il? J'ai une erreur.
Tom Zych
J'ai tapé mon nom en hindi Unicode et il l'a compressé avec succès dans gzip. J'utilise Python 3.2
Future King
@Tom Zych: Cela a probablement quelque chose à voir avec les changements dans 3.2: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel
Je l'ai testé avec ActiveState Python 3.1 et 3.2. Sur ma machine, cela fonctionne dans les deux.
Michał Niklas
1
Pour la compression de fichiers, vous devez toujours ouvrir l'entrée en mode binaire: vous devez pouvoir décompresser le fichier plus tard et obtenir exactement le même contenu. La conversion en Unicode ( str) et inversement n'est pas nécessaire et risque de provoquer des erreurs de décodage ou des décalages entre l'entrée et la sortie.
alexis
96

Il existe une solution plus simple à ce problème.

Vous avez juste besoin d'ajouter un tau mode pour qu'il devienne wt. Cela provoque Python pour ouvrir le fichier en tant que fichier texte et non binaire. Ensuite, tout fonctionnera.

Le programme complet devient ceci:

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
la source
Cela fonctionne-t-il également sur python2? Serait-ce un moyen de faire fonctionner le code sur python2 et python3?
Loïc Faure-Lacroix
Wow, mec tu es bon! Merci! Permettez-moi de vous voter. Cela devrait être la réponse acceptée :))
Loïc
15
L'ajout de "t" peut avoir des effets secondaires. Sur les fichiers Windows encodés en texte, les sauts de ligne ("\ n") seront convertis en CRLF ("\ r \ n").
BitwiseMan
42

Vous ne pouvez pas sérialiser une 'chaîne' Python 3 en octets sans expliquer la conversion en un certain encodage.

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

est peut-être ce que vous voulez. Cela fonctionne également pour python 2.x et 3.x.

Andreas Jung
la source
28

Pour Python 3.x, vous pouvez convertir votre texte en octets bruts via:

bytes("my data", "encoding")

Par exemple:

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

L'objet retourné fonctionnera avec outfile.write.

Skurmedel
la source
9

Ce problème se produit généralement lors du passage de py2 à py3. Dans py2 plaintextest à la fois une chaîne et un type de tableau d'octets . Dans py3 plaintextest seulement une chaîne , et la méthode outfile.write()prend en fait un tableau d'octets lorsqu'elle outfileest ouverte en mode binaire, donc une exception est levée. Modifiez l'entrée plaintext.encode('utf-8')pour résoudre le problème. Continuez à lire si cela vous dérange.

En py2, la déclaration de file.write fait croire que vous avez passé dans une chaîne: file.write(str). En fait , vous étiez de passage dans un tableau d'octets, vous devriez avoir été lu la déclaration comme ceci: file.write(bytes). Si vous le lisez comme ceci, le problème est simple, a file.write(bytes)besoin d'un type d' octets et dans py3 pour obtenir des octets d'une chaîne, vous le convertissez:

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

Pourquoi les documents py2 ont-ils déclaré avoir file.writepris une chaîne? Eh bien dans py2, la distinction de déclaration n'a pas d'importance car:

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

La classe str-bytes de py2 a des méthodes / constructeurs qui la font se comporter comme une classe de chaîne à certains égards et une classe de tableau d'octets à d'autres. Pratique pour file.writenon?:

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

Pourquoi py3 a-t-il cassé ce joli système? Eh bien parce que dans py2, les fonctions de chaîne de base ne fonctionnaient pas pour le reste du monde. Mesurer la longueur d'un mot avec un caractère 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

Pendant tout ce temps que vous pensiez que vous demandiez l' len d'une chaîne dans py2, vous receviez la longueur du tableau d'octets du codage. Cette ambiguïté est le problème fondamental des classes à double droit. Quelle version d'un appel de méthode implémentez-vous?

La bonne nouvelle est que py3 résout ce problème. Il démêle les classes str et bytes . La classe str a des méthodes de type chaîne, la classe d' octets séparée a des méthodes de tableau d'octets:

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

Espérons que le savoir aide à démystifier le problème et rend la douleur migratoire un peu plus facile à supporter.

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

Eh bien, si cela vous est utile en cas de suppression du caractère `` b '' gênant.Si quelqu'un a une meilleure idée, veuillez me suggérer ou n'hésitez pas à me modifier à tout moment ici.Je suis juste un débutant

Tapasit Suesasiton
la source
Vous pouvez également l'utiliser s.encode('utf-8')si pythonique qu'en s.decode('utf-8')remplacement des = bytes("s", "utf-8")
Hans Zimermann
4

Pour Djangoen django.test.TestCasetests unitaires, j'ai changé python2 syntaxe:

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

Pour utiliser la syntaxe Python3 .decode('utf8') :

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