web-dev-qa-db-ja.com

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

この 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

どうしてこれなの?

195
dublintech

base64エンコーディングは8ビットのバイナリバイトデータを受け取り、A-Za-z0-9+/ *の文字のみを使用してエンコードするので、Eメールなどの8ビットのデータをすべて保存しないチャネルを介して送信できます。

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

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

あなたの2番目の例では:

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

すべての文字がASCII文字セットにきちんと収まるので、base64エンコーディングは実際には少し意味がありません。代わりに、ASCIIに変換することができます。

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

もっと簡単です。

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

この場合も同じです。


*ほとんどのbase64フレーバーは最後にパディングとして=を含むかもしれません。さらに、いくつかのbase64の変種は+/以外の文字を使うかもしれません。概要については、ウィキペディアの バリアント要約表 を参照してください。

214
Lennart Regebro

短い答え

bytes-likeオブジェクト(bytesname__、bytearrayname__など)を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では、strname__オブジェクトはCスタイルの文字配列ではありません(つまりnotbyte配列)が、固有のエンコーディングを持たないデータ構造です。 。その文字列をさまざまな方法でエンコード(または解釈)することができます。最も一般的な(そしてPython 3のデフォルト)utf-8は、特にASCIIと後方互換性があるためです(もっとも広く使われているエンコーディングと同様です)。 stringname__を取り、それに.encode()メソッドを呼び出すとそれが起こります。Pythonはutf-8(デフォルトのエンコーディング)で文字列を解釈し、それに対応するバイト配列を提供します。

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

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

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

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

例:

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

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

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バージョンです。ただし、base64encodingは逆方向にエンコードする必要があるため(生データは '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でそのEメールを送信しようとした場合、Eメールは\x04文字が読み取られるとすぐに送信されます。これは、END-OF-TRANSMISSIONの場合はASCIIなので、残りのデータは除外されるためです。トランスミッション.
  2. また、Pythonはデータを直接印刷するときに私のすべての邪悪な制御文字をエスケープするのに十分スマートですが、その文字列がASCIIとしてデコードされるときは、 'msg'がないことがわかります。これは、 'msg'を消去するために3つのBACKSPACEname__文字と3つのSPACEname__文字を使用したためです。したがって、私がそこにEOFname__文字を持っていなくても、エンドユーザーは画面上のテキストから実際の生データに変換することができません。

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

133
Greg Schmit

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

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

必要なものがすべて揃っています。

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-を参照してください。 Python 3.xの文字列の詳細については、of-unicode-vs-8-bit を参照してください。

12
user647772

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

import base64                                                        

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

print(b)    
12
alfredocambera

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

0
Atul6.Singh