https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4


base64ってなんぞ??理解のために実装してみた

base64とは

base64という言葉を聞いたことがあるでしょうか??
base64とは、64進数を意味する言葉で、すべてのデータをアルファベット(a~zA~z)と数字(0~9)、一部の記号(+,/)の64文字で表すエンコード方式です
ただ、データ長を揃えるためにパディングとして末尾に記号の=を使用するので、厳密にはbase64は、65文字の英数字から表現されます
(URLや正規表現のなかでbase64を用いると一部の記号(+,/)は特別な意味を持つことがあるので-_などが用いられることがあります)

なぜbase64をつかうの??

かつての電子メールを送るためのプロトコルSMTPでは、ASCIIといわれる7bitで表現される英数字しか送ることができませんでした
したがって、メールを使って画像や音声などのデータをやりとりしたいと思った時に、英数字しか対応していないSMTPでは、それらのデータを送受信することができませんでした

そこで、すべてのデータを英数字で表すMIME(Multipurpose Internet Mail Extensions)という規格が登場し、その中でbase64というデータの変換方法が定められました
これによって、受信側と送信側がMIMEに則ってエンコード・デコードをすることで、メールを通して画像や音声などの送受信が可能になりました

現在では、JSONなどで特殊文字を含まないように画像データをbase64でエンコードしたり、Webページの表示の際にリクエスト数を減らすためにbase64でエンコードした画像をhtmlにそのまま埋め込むなどの用途で用いられています

base64実装してみた

どの言語にもbase64はサポートされていて、base64の実装をする必要はほとんどありませんが、エンコードの原理の理解を含めて実装してみようと思います

base64の変換方式

base64における処理を簡潔にいうと、
エンコードしたいファイルのバイナリデータを6bitずつ取り出し(足りない分は0を追加する)、
6ビットとAscii文字の変換表を用いて、4文字ずつにする(4文字に満たない場合は=を追加する)

変換アルゴリズム

ここでは、文字列をバイナリ化し、base64でエンコードする例を通して、具体的な処理手順について確認をします

処理手順を以下に示します

1. 変更したい文字列をバイナリ(2進数)に変換する

変換したい文字列"abcdefg"
→0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67(16進数)
→0110 0001, 0110 0010, 0110 0011, 0110 0100, 0110  0101, 0110 0110, 0110 0111(2進数)

2. バイナリを6ビットづつに分割

011000, 010110, 001001, 100011, 011001, 000110, 0101 01, 100110, 011001,11

3. 最後の2ビットが余るので,6ビットになるように0を追加する

011000, 010110, 001001, 100011, 011001, 000110, 0101 01, 100110, 011001, 110000

4. 下図に示す変換表よりビットを文字に変換する

Y W J j Z G V m Z w

5. 4文字に分けた時に、2文字分足りないので=を追加する

YWJj ZGVm Zw==

6. base64の文字列の完成

"YWJjZGVmZw=="

~base64のビット列と英数字の変換表~

10進2進文字10進2進文字10進2進文字10進2進文字
0000000A16010000Q32100000g48110000w
1000001B17010001R33100001h49110001x
2000010C18010010S34100010i50110010y
3000011D19010011T35100011j51110011z
4000100E20010100U36100100k521101000
5000101F21010101V37100101l531101011
6000110G22010110W38100110m541101102
7000111H23010111X39100111n551101113
8001000I24011000Y40101000o561110004
9001001J25011001Z41101001p571110015
10001010K26011010a42101010q581110106
11001011L27011011b43101011r591110117
12001100M28011100c44101100s601111008
13001101N29011101d45101101t611111019
14001110O30011110e46101110u62111110+
15001111P31011111f47101111v63111111/

プログラムで作ってみた

以下のプログラムの実行には、変換表(base64_table.json)が必要なので別途、ダウンロードして使ってください

string2base64.py
#!/usr/bin/env python

import sys
import json

# 1文字が8バイトで表される
BYTE_SIZE = 8

# 文字列textをバイナリの文字列に変換する
def str2bin(s):
    binStr = ""

    #ord関数で文字をASCIIコードへ変換し、
    #シフト演算で各桁のビットを取り出す
    for c in s:
        for i in range(BYTE_SIZE):
            binStr += str((ord(c) >> (BYTE_SIZE - (i + 1))) & 1)
    return binStr

# 文字列sを文字数nで分割したリストを返す
# (例)
# split("abcdef", 2) => [["ab"], ["cd"], ["ef"]]
def split(s, n):
    return [s[i:i+n] for i in range(0, len(s), n)]


# 文字列sを文字数nで分割した時に足りない部分を文字cで埋める
# (例)
# fillInBlank("abcd", 6, "=") => "abcd=="
def fillInBlank(s, n, c):
    mod = len(s) % n

    # 割り切れた場合は何も処理をしない
    if mod == 0: return s 

    # 割り切れなかった場合、残りの部分を埋める
    margin = n - mod
    return s + c * margin

# main関数         
def main():

    # コマンドの引数を受け取る
    argvs = sys.argv
    argc = len(argvs)

    # 引数が1つじゃなかったら無かったら、処理をせず終了
    if argc != 2:
        print "Usage:\n$ python %s CONVERT_STRING" % argvs[0]
        quit()

    # 1. 文字列をバイナリ文字列に変換
    binStr = str2bin(argvs[1])

    # 2. バイナリを6ビットづつに分割
    splitCount  =6
    s = split(binStr, splitCount)

    # 最後の2ビットが余るので,6ビットになるように0を追加する
    s[-1] = fillInBlank(s[-1], 6, "0")

    # 変換表の辞書を読み込み
    tableFile = open("base64_table.json", "r")
    base64Dict = json.loads(tableFile.read())

    result = ""

    for i in s:
        result += base64Dict[i]

    print result

    print fillInBlank(result, 4, "=")

if __name__ == '__main__':
    main()

ファイルの実行

文字列"abcdefg"を引数としてpythonファイルを実行すると次のような結果がでます

Terminal
$ python string2base64.py abcdefg
binary:  01100001011000100110001101100100011001010110011001100111
base64:  YWJjZGVmZw==

WEB便利ツールさんのように、外部ツールと同じ結果を得ることができたので、成功です!!

まとめ

文字列をBase64でエンコードするのは、そもそもの用途からは離れてしまうのですが、確認のために実装を行いました。

車輪の再発明ですが、理解ができて、思った通りに結果になるのは楽しいですね!!

参考文献


+ Recent posts