演習: Base64エンコーディングについて
Base64については前述の通りですが、かなり簡単な実装のため、ここではPythonで確認してみましょう。
ライブラリによる変換
Pythonでは、標準でbase64
というモジュールが用意されており、インポートすることでBase64のエンコード・デコードが可能です。
import base64
Base64でエンコードするには、対象となる文字列はバイト列でないといけません。そのため、文字列をバイト列に変換する必要があります。
# 文字列をバイト列に変換
byte_string = "hello, world".encode("utf-8")
このバイト列をBase64でエンコードするには、base64.b64encode
関数を使います。
# Base64でエンコード
b64_encoded = base64.b64encode(byte_string)
print(b64_encoded) # b'aGVsbG8sIHdvcmxk'
逆にデコードするときは、base64.b64decode
関数を使います。
渡すのはエンコードされた文字列であり、型はバイト列である必要があります。
# Base64でデコード
decoded_bytes = base64.b64decode(b64_encoded)
print(decoded_bytes) # b'hello, world'
デコードされたバイト列を文字列に戻すには、decode
メソッドを使います。
# バイト列を文字列に変換
decoded_string = decoded_bytes.decode("utf-8")
print(decoded_string) # hello, world
これらをまとめると、以下のようになります。
b64-encode.py ※ライブラリのみ使用
# 引数で渡された文字列を、Base64で符号化してください
# augumentparserを使って引数の文字列を受け取ります。
import argparse
import base64
def main():
parser = argparse.ArgumentParser(description="Base64 encode a string")
parser.add_argument("string", type=str, help="String to be encoded")
args = parser.parse_args()
# 文字列をバイトに変換
byte_string = args.string.encode("utf-8")
# Base64で符号化
b64_encoded = base64.b64encode(byte_string)
# バイトを文字列に変換して出力
print(b64_encoded.decode("utf-8"))
if __name__ == "__main__":
main()
b64-decode.py
#!/usr/bin/env python
"""Base64デコードを行う
引数で渡されたBase64で符号化された文字列をデコードして元の文字列に戻す
"""
import argparse
import base64
import binascii
import sys
def main() -> None:
parser = argparse.ArgumentParser(description="Base64 decode a string")
parser.add_argument("string", type=str, help="Base64 encoded string")
args = parser.parse_args()
b64_string = args.string
# Base64でデコード
try:
decoded_bytes = base64.b64decode(b64_string, validate=True)
# バイトを文字列に変換して出力
print(decoded_bytes.decode("utf-8"))
except (binascii.Error, UnicodeDecodeError) as e:
print(f"Error decoding Base64 string: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
アルゴリズムの実装
Base64のアルゴリズムは非常にシンプルなので、自分で実装することもかなり簡単です。 以下は、Base64のエンコードを自分で実装した例です。
b64-encode.py ※アルゴリズムを自前で実装(抜粋)
1def base64_self(s: bytes) -> str:
2 """自分でBase64エンコードしてみる
3
4 Args:
5 s (str): エンコードしたい文字列
6 Returns:
7 str: Base64エンコードされた文字列
8 """
9
10 # バイト列をビット列に変換
11 bits = "".join(format(byte, "08b") for byte in s)
12
13 # 6ビットずつに分割
14 base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
15 # ^^^^^^^^^^^^^^^^^^^^^^^^^^--------------------------^^^^^^^^^^--
16 # 英大文字(26文字) 英小文字(26文字) 数字(10) +/ ということで64種類の文字
17 encoded = ""
18 for i in range(0, len(bits), 6):
19 chunk = bits[i : i + 6]
20 if len(chunk) < 6:
21 chunk += "0" * (6 - len(chunk)) # パディング
22 index = int(chunk, 2)
23 encoded += base64_chars[index]
24
25 # パディング文字を追加
26 padding = (3 - len(s) % 3) % 3
27 encoded += "=" * padding
28
29 return encoded
要所の解説です。
bits = "".join(format(byte, "08b") for byte in s)
ここでは、バイト列をビット列に変換しています。各バイトを8ビットの2進数に変換し、それらを連結しています。 bitsは0か1の文字列になります。
(変換例)
0110100001101111011001110110010101101000011011110110011101100101
これを6ビットずつに区切ります。
for i in range(0, len(bits), 6): # 0,6,12, ...
chunk = bits[i : i + 6] # 6ビット分取得する
if len(chunk) < 6: # 足りないときは末尾に0をパディング
chunk += "0" * (6 - len(chunk)) # パディング
変換先となる文字列は、Base64では以下のように定義されています(英字52文字+数字10文字+記号2文字)。
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
得られたchunkを10進数に変換し、その値を上記の文字列から取り出して連結します。
index = int(chunk, 2)
encoded += base64_chars[index]
最後に、エンコード後の文字列の長さが4の倍数になるように、=
でパディングします。
padding = (3 - len(s) % 3) % 3
encoded += "=" * padding
こうすることで、Base64のエンコードが完了します。