Warum brauche ich 'b', um einen String mit Base64 zu codieren?

258

Nach diesem Python-Beispiel codiere ich einen String als Base64 mit:

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

Aber wenn ich die Führung weglasse b:

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

Ich erhalte folgende Fehlermeldung:

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

Warum ist das?

dublintech
quelle
37
Tatsächlich haben alle Fragen, die "TypeError: erwartete Bytes, nicht str" zurückgeben, dieselbe Antwort.
Lennart Regebro

Antworten:

273

Base64 - Kodierung nimmt 8-Bit - Binär - Byte - Daten und codiert sie verwendet nur die Zeichen A-Z, a-z, 0-9, +, /* so kann es über Kanäle übertragen werden , die nicht alle 8 Bits von Daten werden erhalten, wie beispielsweise E - Mail.

Daher wird eine Zeichenfolge von 8-Bit-Bytes benötigt. Sie erstellen diese in Python 3 mit der b''Syntax.

Wenn Sie das entfernen b, wird es eine Zeichenfolge. Eine Zeichenfolge ist eine Folge von Unicode-Zeichen. base64 hat keine Ahnung, was mit Unicode-Daten zu tun ist, es ist kein 8-Bit. Es sind eigentlich keine Kleinigkeiten. :-)

In Ihrem zweiten Beispiel:

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

Alle Zeichen passen genau in den ASCII-Zeichensatz, und die Base64-Codierung ist daher eigentlich etwas sinnlos. Sie können es stattdessen in ASCII konvertieren, mit

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

Oder einfacher:

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

Welches wäre das gleiche in diesem Fall.


* Die meisten Base64-Aromen können auch ein =am Ende als Polsterung enthalten. Darüber hinaus können einige base64-Varianten andere Zeichen als +und verwenden /. Eine Übersicht finden Sie in der Variantenübersichtstabelle bei Wikipedia.

Lennart Regebro
quelle
174

Kurze Antwort

Sie benötigen ein schieben bytes-likeObjekt ( bytes, bytearrayusw.) an die base64.b64encode()Methode. Hier sind zwei Möglichkeiten:

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

Oder mit einer Variablen:

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

Warum?

In Python 3 sind strObjekte keine Zeichenarrays im C-Stil (es handelt sich also nicht um Byte-Arrays), sondern Datenstrukturen ohne inhärente Codierung. Sie können diese Zeichenfolge auf verschiedene Arten codieren (oder interpretieren). Am häufigsten (und standardmäßig in Python 3) ist utf-8, insbesondere da es abwärtskompatibel mit ASCII ist (obwohl dies die am häufigsten verwendeten Codierungen sind). Das passiert, wenn Sie a nehmen stringund die .encode()Methode darauf aufrufen : Python interpretiert die Zeichenfolge in utf-8 (der Standardcodierung) und stellt Ihnen das Array von Bytes zur Verfügung, dem es entspricht.

Base-64-Codierung in Python 3

Ursprünglich wurde der Fragentitel zur Base-64-Codierung gestellt. Lesen Sie weiter für Base-64-Sachen.

base64Die Codierung verwendet 6-Bit-Binärblöcke und codiert sie mit den Zeichen AZ, az, 0-9, '+', '/' und '=' (einige Codierungen verwenden unterschiedliche Zeichen anstelle von '+' und '/'). . Dies ist eine Zeichenkodierung, die auf dem mathematischen Konstrukt des Radix-64- oder Basis-64-Zahlensystems basiert, aber sehr unterschiedlich ist. Base-64 in Mathe ist ein Zahlensystem wie binär oder dezimal, und Sie nehmen diese Änderung des Radix für die gesamte Zahl vor oder (wenn der Radix, von dem Sie konvertieren, eine Potenz von 2 weniger als 64 ist) in Blöcken von rechts nach links.

Bei der base64Codierung erfolgt die Übersetzung von links nach rechts. Diese ersten 64 Zeichen sind der Grund, warum es als base64 Codierung bezeichnet wird . Das 65. '=' Symbol wird zum Auffüllen verwendet, da die Codierung 6-Bit-Chunks abruft, die Daten, die normalerweise codiert werden sollen, jedoch 8-Bit-Bytes sind, sodass der letzte Chunk manchmal nur zwei oder 4 Bits enthält.

Beispiel:

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

Wenn Sie diese Binärdaten als einzelne Ganzzahl interpretieren, konvertieren Sie sie folgendermaßen in Base-10 und Base-64 ( Tabelle für 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 Durch die Codierung werden diese Daten jedoch folgendermaßen neu gruppiert:

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

'B0ZXN0' ist also mathematisch gesehen die Base-64-Version unserer Binärdatei. Die base64 Codierung muss jedoch in die entgegengesetzte Richtung codiert werden (damit die Rohdaten in 'dGVzdA' konvertiert werden) und hat auch die Regel, anderen Anwendungen mitzuteilen, wie viel Speicherplatz am Ende übrig bleibt. Dies erfolgt durch Auffüllen des Endes mit '=' Symbolen. Die base64Codierung dieser Daten lautet also 'dGVzdA ==', wobei zwei '=' Symbole zur Kennzeichnung von zwei Bitpaaren am Ende entfernt werden müssen, wenn diese Daten decodiert werden, damit sie mit den Originaldaten übereinstimmen.

Lassen Sie uns dies testen, um zu sehen, ob ich unehrlich bin:

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

Warum base64Codierung verwenden?

Angenommen, ich muss einige Daten per E-Mail an jemanden senden, wie diese Daten:

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

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

Ich habe zwei Probleme gepflanzt:

  1. Wenn ich versuchen würde, diese E-Mail unter Unix zu senden, wird die E-Mail gesendet, sobald das \x04Zeichen gelesen wurde, da dies ASCII für END-OF-TRANSMISSION(Strg-D) ist, sodass die verbleibenden Daten von der Übertragung ausgeschlossen werden.
  2. Auch wenn Python klug genug ist, um all meinen bösen Steuerzeichen zu entgehen, wenn ich die Daten direkt drucke, können Sie beim Dekodieren dieser Zeichenfolge als ASCII feststellen, dass die 'msg' nicht vorhanden ist. Das liegt daran, dass ich drei BACKSPACEZeichen und drei SPACEZeichen verwendet habe, um die 'Nachricht' zu löschen. Selbst wenn ich den EOFCharakter dort nicht hätte, wäre der Endbenutzer nicht in der Lage, vom Text auf dem Bildschirm in die realen Rohdaten zu übersetzen.

Dies ist nur eine Demo, die Ihnen zeigt, wie schwierig es sein kann, Rohdaten einfach zu senden. Wenn Sie die Daten in das base64-Format codieren, erhalten Sie genau dieselben Daten, jedoch in einem Format, das sicherstellt, dass sie sicher über elektronische Medien wie E-Mail gesendet werden können.

Greg Schmit
quelle
6
base64.b64encode(s.encode()).decode()ist nicht sehr pythonisch, wenn Sie nur eine Konvertierung von Zeichenfolge in Zeichenfolge wünschen. base64.encode(s)sollte zumindest in python3 ausreichen. Vielen Dank für eine sehr gute Erklärung über Strings und Bytes in Python
MortenB
2
@MortenB Ja, es ist seltsam, aber auf der Oberseite ist sehr klar, was passiert, solange der Ingenieur den Unterschied zwischen Arrays von Bytes und Strings kennt, da es keine einzige Zuordnung (Codierung) zwischen ihnen gibt, wie in anderen Sprachen annehmen.
Greg Schmit
3
@MortenB Würde übrigens base64.encode(s)nicht in Python3 funktionieren; Wollen Sie damit sagen, dass so etwas verfügbar sein sollte? Ich denke, der Grund, warum es verwirrend sein könnte, ist, dass abhängig von der Codierung und dem Inhalt der Zeichenfolge smöglicherweise keine eindeutige Darstellung als Array von Bytes vorhanden ist.
Greg Schmit
Schmitt: Es war nur ein Beispiel dafür, wie einfach es sein sollte. Die häufigsten Anwendungsfälle sollten so sein.
MortenB
1
@MortenB, aber b64 ist nicht nur für Text gedacht, jeder binäre Inhalt kann b64-codiert werden (Audio, Bilder usw.). Wenn Sie es so machen, wie Sie es meiner Meinung nach vorschlagen, wird der Unterschied zwischen Text und Byte-Array noch mehr verborgen, was das Debuggen erschwert. Es verschiebt die Schwierigkeit einfach woanders hin.
Michael Ekoka
32

Wenn die zu codierenden Daten "exotische" Zeichen enthalten, müssen Sie sie in "UTF-8" codieren.

encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))
Alecz
quelle
24

Wenn die Zeichenfolge Unicode ist, ist der einfachste Weg:

import base64                                                        

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

# a: b'Y29tcGxleCBzdHJpbmc6IMOxw6HDqcOtw7PDusOR'

b = base64.b64decode(a).decode("utf-8", "ignore")                    

print(b)
# b :complex string: ñáéíóúÑ
Alfredocambera
quelle
Wirklich nicht der einfachste Weg, aber einer der klarsten Wege, wenn es wichtig ist, welche Codierung für die Übertragung der Zeichenfolge verwendet wird, die Teil des "Protokolls" der Datenübertragung über base64 ist.
xuiqzy
12

Es gibt alles was Sie brauchen:

expected bytes, not str

Der führende bmacht Ihren String binär.

Welche Version von Python verwenden Sie? 2.x oder 3.x?

Edit: Siehe http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit für die blutigen Details von Strings in Python 3.x.


quelle
Danke, ich benutze, 3.x. Warum will Python es explizit in eine Binärdatei konvertieren? Das gleiche in Ruby wäre ... erfordert> "base64" und dann> Base64.encode64 ('zu codierende Daten')
dublintech
2
@dublintech Weil sich (Unicode-) Text von Rohdaten unterscheidet. Wenn Sie eine Textzeichenfolge in Base64 codieren möchten, müssen Sie zuerst die Zeichencodierung (wie UTF-8) bestimmen und dann haben Sie Bytes anstelle von Zeichen, die Sie in einer ascii-sicheren Textform codieren können.
Fortan
2
Dies beantwortet die Frage nicht. Er weiß, dass es mit einem Byte-Objekt funktioniert, aber nicht mit einem String-Objekt. Die Frage ist warum .
Lennart Regebro
@fortran Die Standard-Python3-Zeichenfolgencodierung ist UTF. Ich weiß nicht, warum sie explizit festgelegt werden muss.
xmedeko
0

Das b bedeutet einfach, dass Sie die Eingabe als Bytes oder Bytes-Array und nicht als Zeichenfolge verwenden.

Atul6.Singh
quelle