base64モジュールが提供するb64encode関数とb64decode関数などを使って、文字列やバイナリファイルをBase64形式でエンコード/デコードする方法を解説する。
import base64
# エンコード
di = 'deep insider'
bdi = di.encode('utf-8')
b64encoded = base64.b64encode(bdi)
print(b64encoded) # b'ZGVlcCBpbnNpZGVy'
# b64encode関数に文字列は渡せない
tmp = base64.b64encode(di) # TypeError
# エンコードした結果を文字列にする
s_decoded = b64encoded.decode() # s_decoded = b64encoded.decode('utf-8')
print(s_decoded) # ZGVlcCBpbnNpZGVy
# デコード
decoded = base64.b64decode(b64encoded) # 戻り値はbytes型
print(decoded) # b'deep insider'
decoded = base64.b64decode(s_decoded) # 文字列もデコード可能
print(decoded) # b'deep insider'
# デコードした結果を文字列にする
s_decoded = decoded.decode() # s_decoded = decoded.decode('utf-8')
print(s_decoded) # deep insider
# b'+'の代わりにb'-'を、b'/'の代わりにb'_'を使う
b = bytes([0b00000001, 0b10101101, 0b00111111, 0b11111000])
print(b) # b'\x01\xad?\xf8'
b64encoded = base64.b64encode(b)
print(b64encoded) # b'Aa0/+A=='
urlsafe_b64encoded = base64.b64encode(b, altchars=b'-_')
print(urlsafe_b64encoded) # b'Aa0_-A==':b'+'とb'/'がb'-'とb'_'に置換されている
urlsafe_decoded = base64.b64decode(urlsafe_b64encoded, altchars=b'-_')
print(urlsafe_decoded) # b'\x01\xad?\xf8'
# altcharsパラメーターを指定しないとちゃんとデコードできない
tmp = base64.b64decode(urlsafe_b64encoded)
print(tmp) # b'\x01\xad\x00'
tmp.decode() # UnicodeDecodeError
# base64.urlsafe_b64encode関数とbase64.urlsafe_b64decode関数
urlsafe_b64encoded_ = base64.urlsafe_b64encode(b)
print(urlsafe_b64encoded_) # b'Aa0_-A=='
urlsafe_b64decoded_ = base64.urlsafe_b64decode(urlsafe_b64encoded_)
print(urlsafe_b64decoded_) # b'\x01\xad?\xf8'
# base64.standard_b64encode関数とbase64.standard_b64decode関数
di = 'deep insider'
bdi = di.encode('utf-8')
b64encoded = base64.standard_b64encode(bdi)
print(b64encoded) # b'ZGVlcCBpbnNpZGVy'
decoded = base64.standard_b64decode(b64encoded)
print(decoded) # b'deep insider'
# バイナリファイルをエンコード
with open('kaiketsu.png', 'rb') as f:
data = f.read()
encoded_data = base64.b64encode(data)
# エンコードされたデータをファイルに書き込む
with open('kaiketsu.png.b64', 'wb') as f:
f.write(encoded_data)
# 76文字ごとに改行を入れる
result = b''
skip = 76
idx = 0
while idx < len(encoded_data):
result += encoded_data[idx:idx+skip] + b'\n'
idx += skip
with open('kaiketsu.png.b64', 'wb') as f:
f.write(result)
tmp = [encoded_data[idx:idx+skip] for idx in range(0, len(encoded_data), skip)]
result = b'\n'.join(tmp)
with open('kaiketsu.png.b64', 'wb') as f:
f.write(result)
with open('kaiketsu.png.b64', 'r') as fin, open('test.png', 'wb') as fout:
data = fin.read()
decoded_data = base64.b64decode(data)
fout.write(decoded_data)
Base64はRFC 4648で定められているエンコード/デコード方式であり、バイナリデータをエンコードしてテキスト形式のデータしか扱えない環境(電子メールなど)でそれらを転送する目的などで使われている。
Pythonにはbase64モジュールがあり、これを使うことでBase64形式でのエンコード/デコードが可能になる。本稿ではその基本的な使い方を紹介する。
Base64形式でエンコードするにはbase64.b64encode関数を使用する。以下はこの関数を使って、'deep insider'という文字列をBase64エンコードする例だ。
import base64
di = 'deep insider'
bdi = di.encode('utf-8')
b64encoded = base64.b64encode(bdi)
print(b64encoded) # b'ZGVlcCBpbnNpZGVy'
base64.b64encode関数はbytes型のデータを受け取り、それをBase64形式にエンコードするので、文字列をエンコードするには、それをまずencodeメソッドでバイト列に変換する必要がある。あとはそのバイト列をbase64.b64encode関数に渡すだけだ。
文字列をそのまま渡すと次のようにTypeError例外が発生する。
tmp = base64.b64encode(di) # TypeError
上の実行結果を見ると分かるが、base64.b64encode関数の戻り値はバイト列になっている。そのため、これを文字列として扱いたければ、decodeメソッドで文字列化する必要がある。
s_decoded = b64encoded.decode() # s_decoded = b64encoded.decode('utf-8')
print(s_decoded) # ZGVlcCBpbnNpZGVy
Base64で使われるのは大文字小文字のアルファベット(52文字)と数字(10文字)、アスタリスク「*」とスラッシュ「/」、パディング用のイコール「=」でありASCIIの範囲に含まれるので、バイト列のデコードに際して文字エンコーディングには余り気を使う必要はないだろう。
Base64エンコードされたバイト列をデコードするにはbase64.b64decode関数を使用する。戻り値はバイト列になる点に注意。以下に例を示す。
decoded = base64.b64decode(b64encoded) # 戻り値はbytes型
print(decoded) # b'deep insider'
典型的にはこの例のようにbase64.b64decode関数にバイト列を渡すが、文字列を渡してもよい。以下の例では先ほどbase64.encode関数で得たBase64形式のバイト列を文字列化したものを渡している。
decoded = base64.b64decode(s_decoded) # 文字列もデコード可能
print(decoded) # b'deep insider'
上でも述べたが、Base64形式のデータをデコードした結果はバイト列になるので、それを文字列として扱うにはdecodeメソッドを呼び出すこと。
s_decoded = decoded.decode() # s_decoded = decoded.decode('utf-8')
print(s_decoded) # deep insider
Base64ではエンコード後のバイト列にb'+'とb'/'が含まれる場合がある。これが都合悪いときにはbase64.b64encode関数のaltcharsパラメーターに代替文字を指定できる。このときには必ず2つの文字を指定する必要がある。
例えば、以下のようなバイト列があったとする。
b = bytes([0b00000001, 0b10101101, 0b00111111, 0b11111000])
print(b) # b'\x01\xad?\xf8'
これはBase64エンコードしたときにb'+'とb'/'が含まれるように調整したものだ。これをbase64.b64encode関数に渡すと次のようになる。
b64encoded = base64.b64encode(b)
print(b64encoded) # b'Aa0/+A=='
バイト列にこれらが含まれていることを確認してほしい。altcharsパラメーターを指定してこの関数を呼び出すと次のように置換される。
urlsafe_b64encoded = base64.b64encode(b, altchars=b'-_')
print(urlsafe_b64encoded) # b'Aa0_-A==':b'+'とb'/'がb'-'とb'_'に置換されている
altcharsパラメーターを指定してエンコードした結果を、base64.b64decode関数に渡すときには、もちろんb'+'とb'/'の代わりに使用している文字をaltcharsパラメーターに指定する必要がある。
urlsafe_decoded = base64.b64decode(urlsafe_b64encoded, altchars=b'-_')
print(urlsafe_decoded) # b'\x01\xad?\xf8'
指定しなかった場合、以下の例のように、base64.b64decode関数でデコードはできたように見えても、それを文字列にデコードしようとすると例外が発生する。
tmp = base64.b64decode(urlsafe_b64encoded)
print(tmp) # b'\x01\xad\x00'
tmp.decode() # UnicodeDecodeError
altcharsパラメーターには任意の2文字を指定可能だが、base64.urlsafe_encode関数とbase64.urlsafe_b64decode関数を使えば、b'+'とb'-'、b'/'とb'_'を自動的に変換してくれる。
urlsafe_b64encoded_ = base64.urlsafe_b64encode(b)
print(urlsafe_b64encoded_) # b'Aa0_-A=='
urlsafe_b64decoded_ = base64.urlsafe_b64decode(urlsafe_b64encoded_)
print(urlsafe_b64decoded_) # b'\x01\xad?\xf8'
base64.urlsafe_b64encode関数/base64.urlsafe_b64decode関数と対をなす形になるが、altcharsパラメーターを指定せずにbase64.b64encode関数とbase64.b64decode関数を呼び出すのと同じ効果を持つ関数として、base64.standard_b64encode関数とbase64.standard_b64decode関数も用意されている。
di = 'deep insider'
bdi = di.encode('utf-8')
b64encoded = base64.standard_b64encode(bdi)
print(b64encoded) # b'ZGVlcCBpbnNpZGVy'
decoded = base64.standard_b64decode(b64encoded)
print(decoded) # b'deep insider'
バイナリファイルをBase64でエンコードするには、バイナリモードでファイルを読み込んで、それをbase64.b64encode関数に渡すだけだ。
例としてここでは以下に示す画像をエンコードしてみよう(試したい方は、以下の画像を「kaiketsu.png」というファイル名でダウンロードしてほしい)。
with open('kaiketsu.png', 'rb') as f:
data = f.read()
encoded_data = base64.b64encode(data)
エンコードした結果はバイト列なので、これをファイルに書き出すにはバイナリ書き込みモードでファイルをオープンして、そこにバイト列を書き込めばよい。
with open('kaiketsu.png.b64', 'wb') as f:
f.write(encoded_data)
書き出されたファイルは実際にはテキストファイルとなる。なお、base64.b64encode関数は一定間隔で改行文字を含めるようなことはないので、次のように全てのテキストが1行に書き出されたテキストファイルが出来上がる。
そうではなく、76文字ごとなど、一定の間隔で改行を含めたいのであれば、次のようなコードを実行する。
result = b''
skip = 76
idx = 0
while idx < len(encoded_data):
result += encoded_data[idx:idx+skip] + b'\n'
idx += skip
tmp = [encoded_data[idx:idx+skip] for idx in range(0, len(encoded_data), skip)]
result = b'\n'.join(tmp)
最初のwhileループと次のリスト内包表記では同じことを行っている(が、特に解説はしない)。いずれかを実行して、ファイルに書き出すと次のように一定間隔で改行文字が含められるようになる。
with open('kaiketsu.png.b64', 'wb') as f:
f.write(result)
以下に実行結果を示す。
以下はエンコード後のテキストファイルを読み込んで、バイナリファイルとして復元するコードだ。
with open('kaiketsu.png.b64', 'r') as fin, open('test.png', 'wb') as fout:
data = fin.read()
decoded_data = base64.b64decode(data)
fout.write(decoded_data)
これにより、次のような画像が得られた(エンコード/デコードが機能していることが確認できた)。
Copyright© Digital Advantage Corp. All Rights Reserved.