web-dev-qa-db-fra.com

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

Suivant cet exemple python , je code une 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é la b principale:

>>> 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:\Python32\lib\base64.py", line 56, in b64encode
   raise TypeError("expected bytes, not %s" % s.__class__.__name__)
   TypeError: expected bytes, not str

Pourquoi est-ce?

176
dublintech

l'encodage base64 prend des données en octets binaires sur 8 bits et utilise uniquement les caractères A-Z, a-z, 0-9, +, / * pour qu'il puisse être transmis sur des canaux ne conservant pas tous les 8 bits de données, tels que les courriers électroniques.

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

Si vous supprimez 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 du 8 bits. Ce n'est pas vraiment un morceau, 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 le codage en base64 n'a donc aucun sens. 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'

Ce qui serait la même chose dans ce cas.


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

193
Lennart Regebro

Réponse courte

Vous devez envoyer un objet bytes-like (bytes, bytearray, etc.) à la méthode base64.b64encode(). Voici deux manières:

>>> 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 ne sont donc pas des tableaux d'octets pas), mais plutôt des structures de données dépourvues de 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 codages les plus répandus). C'est ce qui se passe lorsque vous prenez une string et appelez la méthode .encode() dessus: Python interprète la chaîne en utf-8 (l'encodage par défaut) et vous fournit le tableau d'octets auquel elle correspond.

Encodage en base 64 en Python 3

A l'origine, le titre de la question concernait le codage en base 64. Lisez la suite pour des trucs en base 64.

L'encodage base64 prend des morceaux binaires de 6 bits et les code en utilisant les caractères AZ, az, 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 à nombres à base 64 ou à base 64, mais ils sont très différents. Base-64 en mathématiques est un système de numération comme binaire ou décimal, et vous effectuez ce changement de radix sur le nombre entier, ou (si la radix à partir de laquelle vous effectuez la conversion est une puissance de 2 inférieure à 64) en morceaux de droite à droite. la gauche.

Dans l'encodage base64, la traduction se fait de gauche à droite; ces 64 premiers caractères expliquent pourquoi il s’appelle base64encoding. Le 65ème symbole '=' est utilisé pour le remplissage, car l'encodage extrait des blocs de 6 bits, mais les données qu'il est censé encoder sont des octets de 8 bits. Le dernier bloc ne contient parfois que deux ou 4 bits.

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, procédez comme suit pour les convertir en base 10 et base 64 ( table pour 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

base64encoding, cependant, regroupera ces données comme suit:

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 en base 64 de notre binaire, mathématiquement parlant. Cependant, base64encoding doit procéder à l'encodage dans le sens opposé (les données brutes sont donc converties en "dGVzdA") et contient également une règle indiquant à d'autres applications combien d'espace est laissé à la fin. Ceci est fait en complétant la fin avec les symboles '='. Donc, le codage base64 de ces données est 'dGVzdA ==', avec deux symboles '=' pour indiquer que deux paires de bits devront être supprimées de la fin lorsque ces données seront décodées pour les faire correspondre aux données d'origine.

Essayons cela pour voir si je suis malhonnête:

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

Pourquoi utiliser le codage base64?

Disons que je dois envoyer des données à quelqu'un par courrier électronique, 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 j'essayais d'envoyer cet email sous Unix, l'e-mail serait envoyé dès que le caractère \x04 serait lu, car il s'agit de ASCII pour END-OF-TRANSMISSION (Ctrl-D), les données restantes resteraient ainsi en dehors de la transmission. .
  2. De plus, bien que Python soit assez intelligent pour échapper à tous mes caractères de contrôle maléfiques lorsque j'imprime directement les données, lorsque cette chaîne est décodée en ASCII, vous pouvez voir que le "msg" n'y est pas. C'est parce que j'ai utilisé trois caractères BACKSPACE et trois caractères SPACE pour effacer le 'msg'. Ainsi, même si je n'avais pas le caractère EOF, l'utilisateur final ne serait pas en mesure de traduire le texte à l'écran en données brutes réelles.

Ceci est juste une démo pour vous montrer à quel point il peut être difficile d'envoyer simplement des données brutes. Le codage des données au format base64 vous donne exactement les mêmes données, mais dans un format garantissant leur sécurité pour l'envoi sur des supports électroniques tels que les courriers électroniques.

110
Greg Schmit

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"))
27
Alecz

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)    
11
alfredocambera

Il y a tout ce dont vous avez besoin:

expected bytes, not str

La b principale rend votre chaîne binaire.

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

Edit: See 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 de caractères dans Python 3.x

10
user647772

Cela signifie simplement que vous prenez une entrée sous forme de tableau d'octets ou d'octets et non sous forme de chaîne.

0
Atul6.Singh