新しいフォルダー(1)

メモです。よろしくおねがいします。

[文字コード] Base64

Base64

データを64種類の英数字を用い, それ以外の文字を扱うことのできない環境にてマルチバイト文字やバイナリデータを扱うためのエンコーディング方式である.

RFC 2045の6.8項で定義されている.

使用できる文字は, A - Z, a - z, 0 - 9, +, /の64文字とパディング用の記号=の計65文字である.

MIMEによって規定されており, 7ビットのデータしか扱うことのできない電子メールにて広く利用されている.

Base64で変換されたデータは元データの4/3(約133%)となる.

また, MIMEでは76文字毎に改行コードが入る仕様のため, この2バイト分を含めると元データの約137%となる.

生まれた経緯

現在では拡張機能により8bit以上のマルチバイト文字やバイナリデータも送ることが可能だが, かつてはASCII文字で表現できる文字しか送ることができなかった.

そこで, メール上で様々なフォーマットを扱えるようにするMIME(Multipurpose Internet Mail Extensions)という規格が規定され, その中でBase64というエンコーディング方式が定義された.

今日では, JSON特殊文字を含まないように画像データをエンコードしたり, Webページを表示する際にリクエスト数を減らすためにBase64エンコードした画像をHTMLに埋め込むなどの用途で用いられている.

変換表

数値と文字の対応を以下に示す.

10進数 2進数 文字 10進数 2進数 文字 10進数 2進数 文字 10進数 2進数 文字
0 000000 A 16 010000 Q 32 100000 g 48 110000 w
1 000001 B 17 010001 R 33 100001 h 49 110001 x
2 000010 C 18 010010 S 34 100010 i 50 110010 y
3 000011 D 19 010011 T 35 100011 j 51 110011 z
4 000100 E 20 010100 U 36 100100 k 52 110100 0
5 000101 F 21 010101 V 37 100101 l 53 110101 1
6 000110 G 22 010110 W 38 100110 m 54 110110 2
7 000111 H 23 010111 X 39 100111 n 55 110111 3
8 001000 I 24 011000 Y 40 101000 o 56 111000 4
9 001001 J 25 011001 Z 41 101001 p 57 111001 5
10 001010 K 26 011010 a 42 101010 q 58 111010 6
11 001011 L 27 011011 b 43 101011 r 59 111011 7
12 001100 M 28 011100 c 44 101100 s 60 111100 8
13 001101 N 29 011101 d 45 101101 t 61 111101 9
14 001110 O 30 011110 e 46 101110 u 62 111110 +
15 001111 P 31 011111 f 47 101111 v 63 111111 /

変換アルゴリズム

文字列ABCDEFGを例にエンコードを行う.

1. 2進数に変換

元データ 16進数表現 2進数表現
A 0041 01000001
B 0042 01000010
C 0043 01000011
D 0044 01000100
E +0045 01000101
F 0046 01000110
G 0047 01000111

2. 6bitずつに分割

010000 010100 001001 000011 010001 000100 010101 000110 010001 11

3. ゼロパディング

最後の2bitが余っているので, 0でパディングする.

010000 010100 001001 000011 010001 000100 010101 000110 010001 110000

4. 文字に変換

変換表を参考に4文字ずつ変換する.

QUJD REVG Rw

5. パディング

2文字余っているので, =でパディングする.

QUJD REVG Rw==

6. 繋げる

QUJDREVGRw==

Python3による実装

変換表(JSON形式)

{
    "000000": "A",
    "000001": "B",
    "000010": "C",
    "000011": "D",
    "000100": "E",
    "000101": "F",
    "000110": "G",
    "000111": "H",
    "001000": "I",
    "001001": "J",
    "001010": "K",
    "001011": "L",
    "001100": "M",
    "001101": "N",
    "001110": "O",
    "001111": "P",
    "010000": "Q",
    "010001": "R",
    "010010": "S",
    "010011": "T",
    "010100": "U",
    "010101": "V",
    "010110": "W",
    "010111": "X",
    "011000": "Y",
    "011001": "Z",
    "011010": "a",
    "011011": "b",
    "011100": "c",
    "011101": "d",
    "011110": "e",
    "011111": "f",
    "100000": "g",
    "100001": "h",
    "100010": "i",
    "100011": "j",
    "100100": "k",
    "100101": "l",
    "100110": "m",
    "100111": "n",
    "101000": "o",
    "101001": "p",
    "101010": "q",
    "101011": "r",
    "101100": "s",
    "101101": "t",
    "101110": "u",
    "101111": "v",
    "110000": "w",
    "110001": "x",
    "110010": "y",
    "110011": "z",
    "110100": "0",
    "110101": "1",
    "110110": "2",
    "110111": "3",
    "111000": "4",
    "111001": "5",
    "111010": "6",
    "111011": "7",
    "111100": "8",
    "111101": "9",
    "111110": "+",
    "111111": "/"
}

Base64(Python3)

#!/usr/bin/env python

import sys
import json

# 2進数に変換
def str_to_bin(str):
    bin = ''
    for char in str:
        bin += format(ord(char), 'b').zfill(8)
    return bin

# 6bitずつに分割
def split(bin, split_cnt):
    split_bin = [bin[i: i + split_cnt] for i in range(0, len(bin), split_cnt)]
    return split_bin

# ゼロパディング
def zero_padding(split_bin, padding_interval):
    for i in range(padding_interval - 1):
        if len(split_bin) >= padding_interval:
            break
        split_bin += '0'
    return split_bin

# '='でパディング
def equal_padding(base64, padding_interval):
    padding_num = len(base64) % padding_interval
    for i in range(padding_num):
        base64 += '='
    return base64

def main():
    # コマンドライン引数を受け取る
    argvs = sys.argv
    argc = len(argvs)

    # 引数が1つでなければ処理を行わず終了
    if argc != 2:
        print('usage: python %s STRING' %argvs[0])
        sys.exit()

    # 2進数に変換
    bin = str_to_bin(argvs[1])

    # 6bitずつに分割
    split_cnt = 6
    split_bin = split(bin, split_cnt)

    # ゼロパディング
    split_bin[-1] = zero_padding(split_bin[-1], 6)

    # 変換表の読み込み
    base64_table = json.loads(open('base64_table.json', 'r').read())

    # 文字に変換
    base64 = ''
    for i in split_bin:
        base64 += base64_table[i]

    # '='でパディング
    base64 = equal_padding(base64, 4)

    # 出力
    print(base64)

if __name__ == '__main__':
    main()

実行

$ python base64.py ABCDEFG

実行結果

QUJDREVGRw==