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?
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.
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:
- 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 pourEND-OF-TRANSMISSION
(Ctrl-D), de sorte que les données restantes seraient laissées hors de la transmission. - 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 troisSPACE
caractères à effacer le 'msg'. Ainsi, même si je n'avais pas le caractèreEOF
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.
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"))
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
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)