BASE64の変換をする関数を書いてみました。

同じような感じでBASE32とかBASE16とかも作れると思います。RFCを読んでて思ったのですが、BASE64はURLとかファイル名に使っても問題ない文字だけを使うバージョンもあったのですか… 知らなかったです。

module Main (main) where

import Data.Array (Array, (!), listArray)
import Data.Bits ((.&.), (.|.), shiftR, shiftL)
import Data.Char (ord)

base64Table :: Array Int Char
base64Table = 
  listArray (0, 63) $ ['A' .. 'Z'] ++ ['a' .. 'z'] ++ ['0' .. '9'] ++ "+/"

base64Convert :: Int -> Int -> Int -> String
base64Convert c1 c2 c3 =
  let i1 =  (c1 .&. 0xfc) `shiftR` 2
      i2 = ((c1 .&. 0x03) `shiftL` 4) .|. ((c2 .&. 0xf0) `shiftR` 4)
      i3 = ((c2 .&. 0x0f) `shiftL` 2) .|. ((c3 .&. 0xc0) `shiftR` 6)
      i4 =   c3 .&. 0x3f
  in [base64Table ! i1, base64Table ! i2, base64Table ! i3, base64Table ! i4]

base64 :: String -> String
base64 [] = ""

base64 (x1 : []) =
  (take 2 $ base64Convert (ord x1) 0 0) ++ "=="

base64 (x1 : x2 : []) =
  (take 3 $ base64Convert (ord x1) (ord x2) 0) ++ "="

base64 (x1 : x2 : x3 : xs) =
  base64Convert (ord x1) (ord x2) (ord x3) ++ base64 xs

base64SepLn :: Int -> String -> String
base64SepLn n = f n n
  where
    f _ _ [] = ""
    f n 1 (x : xs) = x : '\n' : f n n xs
    f n i (x : xs) = x : f n (i - 1) xs

main :: IO ()
main = putStrLn $ base64SepLn 76 $ base64 "Haskell" -- => "SGFza2VsbA=="

参照: RFC3548