Pourquoi ai-je besoin de 'b' pour encoder une chaîne Python avec Base64?

Suite à cet exemple python , j'encodeune chaîne en Base64 avec:

>>> import base64
>>> encoded = base64.b64encode(b'data to be encoded')
>>> encoded
b'ZGF0YSB0byBiZSBlbmNvZGVk'

Mais, si je laisse de côté le premier b:

>>> encoded = base64.b64encode('data to be encoded')

Je reçois l'erreur suivante:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:Python32libbase64.py", line 56, in b64encode
   raise TypeError("expected bytes, not %s" % s.__class__.__name__)
   TypeError: expected bytes, not str

Pourquoi est-ce?

149
demandé sur Steven Vascellaro 2012-01-18 14:04:30

5 réponses

L'encodage Base64 prend des données d'octets binaires 8 bits et Code uniquement les caractères A-Z, a-z, 0-9, +, /* ainsi, il peut être transmis sur des canaux qui ne conservent pas tous les 8 bits de données, tels que le courrier électronique.

Par conséquent, il veut une chaîne d'octets de 8 bits. Vous les Créez en Python 3 avec la syntaxe b''.

Si vous supprimez le b, il devient une chaîne. Une chaîne est une séquence de caractères Unicode. base64 n'a aucune idée de ce qu'il faut faire avec les données Unicode, ce n'est pas 8 bits. Ce n'est pas vraiment des morceaux, en fait. :-)

Dans votre deuxième exemple:

>>> encoded = base64.b64encode('data to be encoded')

Tous les caractères s'intègrent parfaitement dans le jeu de caractères ASCII, et l'encodage base64 est donc en fait un peu inutile. Vous pouvez le convertir en ascii à la place, avec

>>> encoded = 'data to be encoded'.encode('ascii')

Ou plus simple:

>>> encoded = b'data to be encoded'

, Qui serait la même chose dans ce cas.


* la plupart des saveurs base64 peuvent également inclure un = à la fin comme remplissage. De plus, certaines variantes base64 peuvent utiliser des caractères autre que + et /. Voir le tableau récapitulatif des variantes sur Wikipedia pour un aperçu.

171
répondu Lennart Regebro 2017-01-04 02:33:39

Réponse Courte

Vous devez pousser un objet bytes-like (bytes, bytearray, etc) à la méthode base64.b64encode(). Voici deux façons:

>>> data = base64.b64encode(b'data to be encoded')
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'

, Ou avec une variable:

>>> string = 'data to be encoded'
>>> data = base64.b64encode(string.encode())
>>> print(data)
b'ZGF0YSB0byBiZSBlbmNvZGVk'

Pourquoi?

En Python 3, les objets str ne sont pas des tableaux de caractères de style C (ils sont donc Pas des tableaux d'octets), mais plutôt des structures de données qui n'ont aucun codage inhérent. Vous pouvez encoder cette chaîne (ou l'interpréter) de différentes manières. Le plus commun (et par défaut dans Python 3) est utf-8, d'autant plus qu'il est rétrocompatible avec ASCII (bien que, comme le sont les encodages les plus largement utilisés). C'est ce qui se passe lorsque vous prenez un string et appelez la méthode .encode(): Python interprète la chaîne en utf-8 (l'encodage par défaut) et vous fournit le tableau d'octets auquel il correspond.

Encodage de base 64 en Python 3

À l'origine, le titre de la question posait des questions sur l'encodage en base 64. Lisez la suite pour les trucs de base-64.

base64 l'encodage prend Morceaux binaires 6 bits et les encode en utilisant les caractères A-Z, a-z, 0-9, '+', '/', et ' = '(certains encodages utilisent des caractères différents à la place de ' + 'et'/'). Il s'agit d'un codage de caractères basé sur la construction mathématique du système de nombres radix-64 ou base-64, mais ils sont très différents. Base-64 en mathématiques est un système de nombres comme binaire ou décimal, et vous faites ce changement de base sur le nombre entier, ou (si la base à partir de laquelle vous convertissez est une puissance de 2 inférieure à 64) en morceaux de droite à gauche.

Dans base64 codage, la traduction se fait de gauche à droite; ceux 64 premiers caractères sont pourquoi il est appelé base64 encodage. Le 65ème symbole '=' est utilisé pour le remplissage, puisque l'encodage tire des morceaux de 6 bits, mais les données qu'il est généralement censé coder sont des octets de 8 bits, donc parfois il n'y a que deux ou 4 bits dans le dernier morceau.

Exemple:

>>> data = b'test'
>>> for byte in data:
...     print(format(byte, '08b'), end=" ")
...
01110100 01100101 01110011 01110100
>>>

Si vous interprétez ces données binaires comme un seul entier, alors voici comment vous le feriez convertir en base 10 et en base 64 (table de base-64):

base-2:  01 110100 011001 010111 001101 110100 (base-64 grouping shown)
base-10:                            1952805748
base-64:  B      0      Z      X      N      0

base64 encodage, cependant, sera de nouveau groupe de données comme ceci:

base-2:  011101  000110  010101 110011 011101 00(0000) <- pad w/zeros to make a clean 6-bit chunk
base-10:     29       6      21     51     29      0
base-64:      d       G       V      z      d      A

Donc,' B0ZXN0 ' est la version de base 64 de notre binaire, mathématiquement parlant. Cependant, base64 encoding doit faire l'encodage dans la direction opposée (de sorte que les données brutes sont converties en 'dGVzdA') et a également une règle pour dire aux autres applications combien d'espace est laissé à la fin. Ceci est fait en remplissant le terminez par les symboles'='. Ainsi, l'encodage base64 de ces données est ' dGVzdA==', avec deux symboles ' = ' pour signifier deux paires de bits devront être supprimés de la fin lorsque ces données seront décodées pour les faire correspondre aux données d'origine.

Testons ceci pour voir si je suis malhonnête:

>>> encoded = base64.b64encode(data)
>>> print(encoded)
b'dGVzdA=='

Pourquoi utiliser l'encodage base64?

Disons que je dois envoyer des données à quelqu'un par e-mail, comme ces données:

>>> data = b'\x04\x6d\x73\x67\x08\x08\x08\x20\x20\x20'
>>> print(data.decode())

>>> print(data)
b'\x04msg\x08\x08\x08   '
>>>

Il y a deux problèmes que j'ai plantés:

  1. Si Je essayé d'envoyer cet e-mail sous Unix, l'e-mail serait envoyé dès que le caractère \x04 a été lu, car C'est ASCII pour END-OF-TRANSMISSION (Ctrl-D), de sorte que les données restantes seraient laissées hors de la transmission.
  2. en outre, alors que Python est assez intelligent pour échapper à tous mes caractères de contrôle maléfiques lorsque j'imprime les données directement, lorsque cette chaîne est décodée en ASCII, vous pouvez voir que le ' msg ' n'est pas là. C'est parce que j'ai utilisé trois BACKSPACE caractères et trois SPACE caractères à effacer le 'msg'. Ainsi, même si je n'avais pas le caractère EOF là, l'utilisateur final ne serait pas capable de traduire du texte à l'écran vers les données réelles et brutes.

Ceci est juste une démo pour vous montrer à quel point il peut être difficile d'envoyer simplement des données brutes. Encoder les données au format base64 vous donne exactement les mêmes données, mais dans un format qui garantit qu'il est sûr pour l'envoi sur des supports électroniques tels que le courrier électronique.

94
répondu Greg Schmit 2018-01-25 00:14:23

Si les données à encoder contiennent des caractères "exotiques", je pense que vous devez encoder en "UTF-8"

encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))
24
répondu Alecz 2013-11-11 20:11:35

Il y a tout ce dont vous avez besoin:

expected bytes, not str

Le premier b rend votre chaîne binaire.

Quelle version de Python utilisez-vous? 2.x ou 3.x?

Edit: Voir http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit pour les détails sanglants des chaînes en Python 3.x

10
répondu 2012-01-18 10:13:53

Si la chaîne est unicode, le moyen le plus simple est:

import base64                                                        

a = base64.b64encode(bytes(u'complex string: ñáéíóúÑ', "utf-8"))
b = base64.b64decode(a).decode("utf-8", "ignore")                    

print(b)    
8
répondu alfredocambera 2016-08-05 18:36:40