2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby開発Advent Calendar 2022

Day 6

Base64を実装して理解する

Last updated at Posted at 2022-12-05

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文字グループを作ります。
base64.png
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

参考文献:
https://ja.wikipedia.org/wiki/Base64

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?