Base64で文字列をエンコードするために 'b'が必要なのはなぜですか?


258

このpythonの例に従って、文字列をBase64としてエンコードします。

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

しかし、私が先頭を省略した場合b

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

次のエラーが発生します。

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

どうしてこれなの?


38
実際に、「TypeError:strではなく、予想されるバイト数」を返すすべての質問は同じ答えを持っています。
Lennart Regebro 2012年

回答:


274

base64エンコーディングは、文字のみを使用する8ビットのバイナリバイトデータと符号化を要しA-Za-z0-9+/*は電子メールなどのデータを、すべての8ビットを保持していないチャネルを介して送信することができるように。

したがって、8ビットバイトの文字列が必要です。それらをPython 3でb''構文を使用して作成します。

を削除するbと、文字列になります。文字列は、Unicode文字のシーケンスです。base64は、Unicodeデータをどのように処理するかがわかりません。8ビットではありません。実際、それは実際には少しもありません。:-)

2番目の例では:

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

すべての文字がASCII文字セットにきちんと収まるので、base64エンコードは実際には少し無意味です。代わりに、アスキーに変換できます

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

またはより単純:

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

この場合も同じです。


*ほとんどのbase64フレーバーは=、末尾にパディングとしてa を含めることもできます。さらに、一部のbase64バリアントは+および以外の文字を使用する場合があり/ます。概要については、Wikipediaのバリアントの要約表を参照してください。


174

短い答え

あなたはプッシュする必要があるbytes-likeオブジェクト(bytesbytearrayに、等)base64.b64encode()する方法。2つの方法は次のとおりです。

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

または変数を使って:

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

どうして?

Python 3では、strオブジェクトはCスタイルの文字配列ではないため(バイト配列ではありません)、固有のエンコーディングを持たないデータ構造です。その文字列は、さまざまな方法でエンコード(または解釈)できます。最も一般的(そしてPython 3のデフォルト)はutf-8です。これは、ASCIIと下位互換性があるためです(ただし、最も広く使用されているエンコーディングと同様)。これは、a stringを受け取ってその.encode()メソッドを呼び出すと発生します。Pythonは文字列をutf-8(デフォルトのエンコーディング)で解釈し、それに対応するバイトの配列を提供します。

Python 3のBase-64エンコーディング

元々、質問のタイトルはBase-64エンコーディングについて尋ねていました。Base-64について読んでください。

base64エンコーディングは6ビットのバイナリチャンクを受け取り、AZ、az、0-9、「+」、「/」、および「=」の文字を使用してエンコードします(一部のエンコーディングは「+」と「/」の代わりに異なる文字を使用します)。 。これは、基数64または基数64の数体系の数学的構造に基づいた文字エンコードですが、非常に異なります。数学のBase-64は、2進数や10進数のような数体系であり、基数のこの変更は整数全体で行うか、または(変換元の基数が64未満の2の累乗の場合)右から左。

ではbase64エンコーディング、翻訳は左から右に行われます。これらの最初の64文字が、base64 エンコーディングと呼ばれる理由です。65番目の「=」記号はパディングに使用されます。これは、エンコーディングが6ビットのチャンクをプルするためですが、通常、エンコードするデータは8ビットバイトであるため、最後のチャンクに2ビットまたは4ビットしかない場合があります。

例:

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

そのバイナリデータを単一の整数として解釈する場合、次の方法でそれをbase-10およびbase-64(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 ただし、encodingは、このデータを次のように再グループ化します。

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」は数学的に言えば、バイナリのbase-64バージョンです。ただし、base64 エンコードは逆方向のエンコードを行う必要があるため(生データが「dGVzdA」に変換されるため)、最後に残りのスペースを他のアプリケーションに通知するルールもあります。これは、「=」記号で終わりを埋めることによって行われます。したがって、base64このデータのエンコードは「dGVzdA ==」であり、2つの「=」記号を使用して、2組のビットを元のデータと一致させるためにこのデータをデコードするときに、最後から削除する必要があります。

これをテストして、私が不正直かどうかを確認しましょう。

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

base64エンコーディングを使用する理由

次のようなデータをメールで誰かに送信する必要があるとします。

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

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

私が植えた2つの問題があります:

  1. そのメールをUnixで送信しようとすると、\x04文字が読み取られるとすぐにメールが送信されます。これは、END-OF-TRANSMISSION(Ctrl-D)のASCII であるため、残りのデータは送信されません。
  2. また、Pythonはデータを直接出力するときに邪悪な制御文字をすべてエスケープするのに十分スマートですが、その文字列がASCIIとしてデコードされると、「msg」がそこにないことがわかります。これは、「msg」を消去するために3 BACKSPACE文字と3 SPACE文字を使用したためです。したがって、EOFそこに文字がなかったとしても、エンドユーザーは画面上のテキストから実際の生データに翻訳することができません。

これは単に生データを送信するのがどれほど難しいかを示すデモにすぎません。データをbase64形式にエンコードすると、まったく同じデータが得られますが、電子メールなどの電子メディアを介して安全に送信できる形式になります。


6
base64.b64encode(s.encode()).decode()あなたが望むすべてが文字列から文字列への変換であるとき、それはあまりpythonicではありません。base64.encode(s)少なくともpython3では十分です。Pythonの文字列とバイトについて非常に良い説明をありがとう
MortenB

2
@MortenBええ、それは奇妙ですが、バイトと文字列の配列の違いを他の言語のように単一のマッピング(エンコード)がないため、エンジニアがバイトと文字列の配列の違いを認識している限り、何が起こっているのかは明らかです仮定します。
グレッグシュミット

3
@MortenBちなみに、Python3 base64.encode(s)では動作しません。そのようなものが利用可能であるべきだとあなたは言っていますか?紛らわしいのは、エンコーディングと文字列の内容によってはs、バイトの配列として一意の表現が1 つない場合があるためです。
グレッグシュミット2018

シュミット:それはそれがいかに単純であるべきかの単なる例でした。最も一般的なユースケースはそのようにする必要があります。
MortenB 2018

1
@MortenBですが、b64は単なるテキスト用ではなく、バイナリコンテンツをb64でエンコードできます(オーディオ、画像など)。あなたが私の意見で提案するようにそれを機能させると、テキストとバイト配列の違いがさらに隠され、デバッグが難しくなります。それは単に難易度を別の場所に移動するだけです。
Michael Ekoka

32

エンコードするデータに「エキゾチック」な文字が含まれている場合、「UTF-8」でエンコードする必要があると思います

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

24

文字列がUnicodeの場合、最も簡単な方法は次のとおりです。

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: ñáéíóúÑ

実際には最も簡単な方法ではありませんが、最も明確な方法の1つです。base64を介したデータ送信の「プロトコル」の一部である文字列の送信にどのエンコーディングを使用するかが重要な場合です。
xuiqzy

12

必要なものはすべてあります。

expected bytes, not str

リーディングbは文字列をバイナリにします。

Pythonのどのバージョンを使用していますか?2.xまたは3.x?

編集:参照http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bitを Pythonで文字列の血みどろの詳細については、 3.x


3.xを使用していただきありがとうございます。なぜPythonはそれを明示的にバイナリに変換したいのですか?Rubyでも同じですが、> "base64"が必要で、次に> Base64.encode64( 'data to be
encrypted

2
@dublintech(Unicode)テキストは生データとは異なるため。Base64でテキスト文字列をエンコードする場合は、最初に文字エンコード(UTF-8など)を決定する必要があります。次に、文字ではなくバイトを使用して、テキストのASCIIセーフ形式でエンコードできます。
Fortran

2
これは質問の答えにはなりません。彼はそれがbytesオブジェクトでは機能するが、stringオブジェクトでは機能しないことを知っています。問題はその理由です。
Lennart Regebro 2012年

@fortranデフォルトのPython3文字列エンコーディングはUTFですが、なぜ明示的に設定する必要があるのか​​わかりません。
xmedeko 2016

0

そのbは、入力を文字列としてではなく、バイトまたはバイト配列として受け取ることを意味します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.