Dlaczego potrzebuję 'b', aby zakodować ciąg za pomocą Base64?

Podążając za tym przykład Pythona , koduję łańcuch jako Base64 z:

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

Ale jeśli pominę wiodącą b:

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

Dostaję następujący błąd:

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
Dlaczego tak jest?
Author: martineau, 2012-01-18

5 answers

Kodowanie Base64 pobiera 8-bitowe binarne dane bajtowe i koduje tylko znaki A-Z, a-z, 0-9, +, /* więc może być przesyłany za pośrednictwem kanałów, które nie zachowują wszystkich 8-bitowych danych, takich jak e-mail.

Stąd chce ciąg 8-bitowych bajtów. Tworzysz je w Pythonie 3 za pomocą składni b''.

Jeśli usuniesz b, stanie się on ciągiem znaków. Ciąg znaków jest sekwencją znaków Unicode. base64 nie ma pojęcia, co zrobić z danymi Unicode, nie jest 8-bitowy. Właściwie to nie są żadne kawałki. :-)

W drugim przykładzie:

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

Wszystkie znaki idealnie pasują do zestawu znaków ASCII, a kodowanie base64 jest więc trochę bezcelowe. Możesz przekonwertować go do ascii za pomocą

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

Lub prościej:

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

Czyli to samo w tym przypadku.


* Większość smaków base64 może również zawierać = na końcu jako wyściółkę. Ponadto niektóre warianty base64 mogą używać znaków inne niż + i /. Zobacz tabelę podsumowującą warianty W Wikipedii, aby uzyskać przegląd.

 291
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 02:33:39

Krótka Odpowiedź

Musisz wcisnąć bytes-like obiekt (bytes, bytearray, etc) DO METODY base64.b64encode(). Oto dwa sposoby:

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

Lub ze zmienną:

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

W Pythonie 3, str obiekty nie są tablicami znaków w stylu C (są więc tablicami nie bajtów), ale raczej są strukturami danych, które nie mają żadnego właściwego kodowania. Możesz zakodować ten ciąg znaków (lub zinterpretować go) na wiele sposobów. Najczęściej (i domyślnie w Pythonie 3) jest utf-8, zwłaszcza, że jest wstecznie kompatybilny z ASCII (chociaż, jak są najczęściej używane kodowania). To właśnie się dzieje, gdy bierzesz string i wywołujesz na nim metodę .encode(): Python interpretuje łańcuch znaków w utf-8 (domyślne kodowanie) i dostarcza tablicę bajtów, do których odpowiada.

Kodowanie Base-64 w Pythonie 3

Pierwotnie tytuł pytania zadawane o kodowanie Base-64. Czytaj dalej w bazie 64 rzeczy.

base64 kodowanie zajmuje 6-bitowe binarne kawałki i koduje je za pomocą znaków A-z, a-z, 0-9, '+', '/', i " = "(niektóre kodowania używają różnych znaków zamiast " + "i"/"). Jest to kodowanie znaków, które opiera się na matematycznej konstrukcji systemu liczbowego radix-64 lub base-64, ale są one bardzo różne. Baza-64 w matematyce jest systemem liczbowym, takim jak binarny lub dziesiętny, i robisz tę zmianę radix na całej liczbie, lub (jeśli radix, z którego konwertujesz, jest potęgą 2 mniejszą niż 64) w kawałkach od prawej do w lewo.

W kodowaniu base64 tłumaczenie odbywa się od lewej do prawej; te pierwsze 64 znaki są powodem, dla którego nazywa się base64 kodowanie . 65. symbol ' = ' jest używany do wypełnienia, ponieważ kodowanie pobiera 6-bitowe kawałki, ale dane, które zwykle ma zakodować, to 8-bitowe bajty, Więc czasami są tylko dwa lub 4 bity w ostatnim kawałku.

Przykład:

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

Jeśli zinterpretujesz dane binarne jako pojedynczą liczbę całkowitą, to w ten sposób przekonwertujesz je na base-10 i base-64 (tabela dla bazy-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 kodowanie spowoduje jednak ponowne zgrupowanie tych danych w ten sposób:

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

Więc, 'B0ZXN0' jest wersją bazy 64 naszej binarnej, matematycznie mówiąc. Jednakże, base64 kodowanie musi wykonywać kodowanie w przeciwnym kierunku (więc surowe dane są konwertowane do 'dGVzdA'), a także ma regułę informującą inne aplikacje, ile miejsca zostało na końcu. Odbywa się to poprzez wypełnienie końca znakiem'=' symbole. Tak więc kodowanie base64 tych danych to ' dGVzdA==', z dwoma symbolami ' = ' oznaczającymi dwie pary bitów będzie musiało zostać usunięte z końca, gdy te dane zostaną dekodowane, aby pasowały do oryginalnych danych.

Przetestujmy to, aby zobaczyć, czy nie jestem nieuczciwy:

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

Po co używać kodowania base64?

Powiedzmy, że muszę wysłać komuś dane przez e-mail, takie jak te dane:

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

Są dwa problemy, które posadziłem:

  1. If I tried to send that e-mail w Unixie, e-mail wysyłał się natychmiast po odczytaniu znaku \x04, ponieważ jest to ASCII dla END-OF-TRANSMISSION (Ctrl-D), więc pozostałe dane nie były przesyłane.
  2. również, podczas gdy Python jest wystarczająco inteligentny, aby uciec od wszystkich moich złych znaków kontrolnych, kiedy drukuje DANE bezpośrednio, kiedy ten ciąg jest dekodowany jako ASCII, możesz zobaczyć, że 'msg' nie istnieje. To dlatego, że użyłem trzech BACKSPACE znaków i trzech SPACE znaków, aby usunąć 'msg'. Tak więc, nawet jeśli jeśli nie ma tam znaku EOF, użytkownik końcowy nie byłby w stanie przetłumaczyć z tekstu na prawdziwe, surowe dane.

To tylko demo, aby pokazać, jak trudno jest po prostu wysłać surowe dane. Kodowanie danych do formatu base64 daje dokładnie te same dane, ale w formacie, który zapewnia, że jest bezpieczny do wysyłania za pośrednictwem mediów elektronicznych, takich jak e-mail.

 183
Author: Greg Schmit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-22 00:27:43

Jeśli dane do zakodowania zawierają "egzotyczne" znaki, myślę, że musisz zakodować w "UTF-8"

encoded = base64.b64encode (bytes('data to be encoded', "utf-8"))
 33
Author: Alecz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 20:11:35

Jeśli łańcuch jest Unicode najprostszym sposobem jest:

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: ñáéíóúÑ
 26
Author: alfredocambera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-11 07:31:58

There is all you need:

expected bytes, not str

Początek b sprawia, że Twój ciąg jest binarny.

Jakiej wersji Pythona używasz? 2.x lub 3.x?

Edit: Zobacz http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit dla krwawych szczegółów ciągów w Pythonie 3.x

 12
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-18 10:13:53