Base64とは
あらゆるデータ:テキストデータ、バイナリデータ(画像、音声など)から64文字をを用いてエンコードする方式です。
- [A-Z]: 26文字
- [a-z]: 26文字
- [0-9]: 10文字
- [+ /]: 2文字
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
+/
Base64が誕生した理由は上記以外の文字を扱うことの出来ない通信環境にてマルチバイト文字(漢字など)やバイナリデータを扱うためです。7ビットのデータしか扱うことの出来ない電子メールにて広く利用されています。
バイナリデータなどをやりとりするには、文字列に変換する必要があるので、文字列を変換した時に4文字グループごとにまとめ、足りない部分は=
で埋めるため、厳密に言えば=
も含めて65文字が使用されています。
URLや正規表現の中でBase64を用いると+
や/
は特別な意味を持つので、+
は-
、/
は_
に変換する場合があります。
Base64のアルゴリズム
エンコード
変換したいデータを2進数で表し、6ビットずつ区切り、6ビットに足りない場合は0
で埋めます。
例えば文字a
は次のように変換します。
a => 01100001 => 011000 01
最後は2ビットしかないので、0
で埋めると011000 010000
になります。
変換テーブルを使用してインデックスから文字を取得し、4文字グループを作ります。
4文字に足りない場合は=
で埋めます。
011000(24) 010000(16) => Y Q
、長さは2しかないので、=
で埋めるとYQ==
になります。
デコード
エンコードされた文から=
を取り除き、上記の変換テーブルを使用して文字からインデックスを取得し、2進数で表します。
YQ== => YQ => Y Q => 24 16 => 011000 010000 => 011000010000
8ビットごとにASCIIコードを用いて変換し、余った0
は捨てます。
011000010000 => 01100001 0000 => 01100001(97) => a
Rubyで実装してみます
class MyBase64
# 変換テーブル
ConvertTable = [*'A'..'Z', *'a'..'z', *'0'..'9', '+', '/']
class << self
def encode(str)
# 文字列をバイナリに変換
# rjustは8桁の文字列を作るため足りない部分を0で左側に詰めます。
# https://docs.ruby-lang.org/ja/latest/method/String/i/rjust.html
binary_str = str.bytes.map { |byte| byte.to_s(2).rjust(8, '0') }.join
# 6ビットに足りない部分を0で埋める
binary_str += '0' while binary_str.size % 6 != 0
# 6ビットごとに分割
result = binary_str.scan(/.{6}/).map { |chunk| ConvertTable[chunk.to_i(2)] }.join
# 長さは4より少ないときは=で埋める
result += '=' while result.size % 4 != 0
result
end
def decode(str)
# =を除去
str.gsub!(/[=]/, '')
# 変換テーブルを逆順にする(文字からインデックスを取得)
binary_str = str.chars.map { |ch| ConvertTable.index(ch).to_s(2).rjust(6, '0') }.join
# 余った0を除去
binary_str.chop! while binary_str.size % 8 != 0
# 8ビットごとに分割、ACSIIコードに戻す
binary_str.scan(/.{8}/).map { |chunk| chunk.to_i(2).chr }.join
end
def encode_urlsafe(str)
encode(str).tr('+/', '-_')
end
def decode_urlsafe(str)
decode(str.tr('-_', '+/'))
end
end
end
# 標準ライブラリと動作検証
require 'base64'
input = gets.chop #fはkjfはskfはsdkhf2y3r81923892yfはs
a = MyBase64.encode(input) #ZuOBr2tqZuOBr3NrZuOBr3Nka2hmMnkzcu+8mO+8ke+8me+8ku+8k++8mO+8me+8knlm44Gvcw==
b = Base64.strict_encode64(input) #ZuOBr2tqZuOBr3NrZuOBr3Nka2hmMnkzcu+8mO+8ke+8me+8ku+8k++8mO+8me+8knlm44Gvcw==
c = MyBase64.encode_urlsafe(input) #ZuOBr2tqZuOBr3NrZuOBr3Nka2hmMnkzcu-8mO-8ke-8me-8ku-8k--8mO-8me-8knlm44Gvcw==
d = Base64.urlsafe_encode64(input) #ZuOBr2tqZuOBr3NrZuOBr3Nka2hmMnkzcu-8mO-8ke-8me-8ku-8k--8mO-8me-8knlm44Gvcw==
puts a, b, c, d
e = MyBase64.decode(a) #fはkjfはskfはsdkhf2y3r81923892yfはs
f = Base64.strict_decode64(b) #fはkjfはskfはsdkhf2y3r81923892yfはs
g = MyBase64.decode_urlsafe(c) #fはkjfはskfはsdkhf2y3r81923892yfはs
h = Base64.urlsafe_decode64(d) #fはkjfはskfはsdkhf2y3r81923892yfはs
puts e, f, g, h