HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

各言語でbase64エンコード/デコード

手元にある各言語で、base64エンコード/デコードをしてみたメモ。

要件は以下の通り。

  • 標準入力は以下の通り構成される
    • 1行目はASCII文字(空白文字を含む)からなる文字列が与えられる
    • 2行目はbase64エンコードされた文字列が与えられる
    • 3行目以降はMIME形式に従ってbase64エンコードされた文字列が与えられる
  • 入力として与えられる「base64エンコードされた文字列」をbase64デコードしても、得られるのはASCII文字(空白文字を含む)のみからなる文字列とする
  • 入力される各行の長さは不明とする(つまり上限設定なし)
  • 入力の不正チェックは不要(上記仕様に従った入力が必ず与えられるとしてよい)
  • 標準出力には以下を出力する
    • 入力の1行目をMIME形式に従ってbase64エンコードした結果の文字列(複数行にわたることがある)
    • 入力の1行目を改行無しでbase64エンコードした結果の文字列
    • 入力の2行目をbase64デコードした結果の文字列
    • 入力の3行目以降をbase64デコードした結果の文字列
    • 各出力の末尾には改行コードを入れること

環境

手元にあるものということで、環境は以下のものに限定する。

なお、以下の言語は自前実装しなきゃならなくて辛いので割愛。

入力

od -cした結果で示す。

0000000   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p
0000020   q   r   s   t   u   v   w   x   y   z   A   B   C   D   E   F
0000040   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V
0000060   W   X   Y   Z   0   1   2   3   4   5   6   7   8   9  \n   Y
0000100   W   J   j   Z   G   V   m   Z   2   h   p   a   m   t   s   b
0000120   W   5   v   c   H   F   y   c   3   R   1   d   n   d   4   e
0000140   X   p   B   Q   k   N   E   R   U   Z   H   S   E   l   K   S
0000160   0   x   N   T   k   9   Q   U   V   J   T   V   F   V   W   V
0000200   1   h   Z   W   j   A   x   M   j   M   0   N   T   Y   3   O
0000220   D   k   =  \n   Y   W   J   j   Z   G   V   m   Z   2   h   p
0000240   a   m   t   s   b   W   5   v   c   H   F   y   c   3   R   1
0000260   d   n   d   4   e   X   p   B   Q   k   N   E   R   U   Z   H
0000300   S   E   l   K   S   0   x   N   T   k   9   Q   U   V   J   T
0000320   V   F   V   W   V   1   h   Z   W   j   A   x   M   j   M   0
0000340  \r  \n   N   T   Y   3   O   D   k   =  \r  \n
0000354

人間に分かりやすく示すと大体以下のような感じ。

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODk=
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0
NTY3ODk=

ただし3行目以降の各行は「CR LF」で終わっていることに注意。

出力

これもod -cした結果で示すと以下のような感じ。

0000000   Y   W   J   j   Z   G   V   m   Z   2   h   p   a   m   t   s
0000020   b   W   5   v   c   H   F   y   c   3   R   1   d   n   d   4
0000040   e   X   p   B   Q   k   N   E   R   U   Z   H   S   E   l   K
0000060   S   0   x   N   T   k   9   Q   U   V   J   T   V   F   V   W
0000100   V   1   h   Z   W   j   A   x   M   j   M   0  \r  \n   N   T
0000120   Y   3   O   D   k   =  \r  \n   Y   W   J   j   Z   G   V   m
0000140   Z   2   h   p   a   m   t   s   b   W   5   v   c   H   F   y
0000160   c   3   R   1   d   n   d   4   e   X   p   B   Q   k   N   E
0000200   R   U   Z   H   S   E   l   K   S   0   x   N   T   k   9   Q
0000220   U   V   J   T   V   F   V   W   V   1   h   Z   W   j   A   x
0000240   M   j   M   0   N   T   Y   3   O   D   k   =  \n   a   b   c
0000260   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s
0000300   t   u   v   w   x   y   z   A   B   C   D   E   F   G   H   I
0000320   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y
0000340   Z   0   1   2   3   4   5   6   7   8   9  \n   a   b   c   d
0000360   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t
0000400   u   v   w   x   y   z   A   B   C   D   E   F   G   H   I   J
0000420   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z
0000440   0   1   2   3   4   5   6   7   8   9  \n
0000453

人間に分かりやすく示すと大体以下のような感じ。

YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0
NTY3ODk=
YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODk=
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

ただし、上の例でいうところの1~2行目の末尾は「CR LF」で終わっていることに注意。

Java

import java.io.IOException;
import java.io.PrintStream;
import java.util.Base64;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 今回はASCII文字しかない前提なので手抜きでScannerを使っている
        try (Scanner sc = new Scanner(System.in);
            PrintStream out = new PrintStream(System.out)
        ) {
            String line1 = sc.nextLine();

            // MIME format(encode)
            Base64.Encoder mimeEncoder = Base64.getMimeEncoder(/*76, "\n".getBytes()*/);
            out.write(mimeEncoder.encode(line1.getBytes()));
            out.print("\r\n"); // MimeEncoderでは最後の行にCR LFを付加しないので、その分を補う

            // no wrap format(encode)
            Base64.Encoder encoder = Base64.getEncoder();
            out.write(encoder.encode(line1.getBytes()));
            out.println();

            // no wrap format(decode)
            String line21 = sc.nextLine();
            Base64.Decoder decoder = Base64.getDecoder();
            out.write(decoder.decode(line21));
            out.println();

            // MIME format(decode)
            Base64.Decoder mimeDecoder = Base64.getMimeDecoder();
            StringBuilder sb = new StringBuilder();
            while (sc.hasNextLine()) {
                sb.append(sc.nextLine());
                sb.append("\r\n"); // わざとMIME形式を復元してみる
            }
            String line22 = sb.toString();
            out.write(mimeDecoder.decode(line22));
            out.println();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PHP

<?php

$lines = file('php://stdin');

// MIME format(encode)
$line1 = array_shift($lines);
$line1 = preg_replace("/[\r\n]/", "", $line1);
print chunk_split(base64_encode($line1)/*, 76, "\n"*/);

// no wrap format(encode)
print base64_encode($line1) . PHP_EOL;

// no wrap format(decode)
$line21 = array_shift($lines);
$line21 = preg_replace("/[\r\n]/", "", $line21);
print base64_decode($line21) . PHP_EOL;

// MIME format(decode)
$line22 = implode('', $lines);
$line22 = preg_replace("/[\r\n]/", "", $line22);
print base64_decode($line22) . PHP_EOL;

Python 2

import sys
import base64

line1 = sys.stdin.readline()
line1 = line1[0:len(line1) - 1] # remove LF
res1 = base64.b64encode(line1)

# MIME format(encode)
for i in range(0, len(res1), 76):
    print res1[i:i + 76] + "\r"

# no wrap format(encode)
print res1

# no wrap format(decode)
line21 = sys.stdin.readline()
print base64.b64decode(line21)

# MIME format(decode)
line22 = ''
while True:
    tmp = sys.stdin.readline()
    if tmp == '':
        break
    line22 = line22 + tmp
print base64.b64decode(line22)

Python 3

import sys
import base64

line1 = sys.stdin.buffer.readline()
line1 = line1[0:len(line1) - 1] # remove LF
res1 = base64.b64encode(line1)

# MIME format(encode)
for i in range(0, len(res1), 76):
    print(res1[i:i + 76].decode() + "\r")

# no wrap format(encode)
print(res1.decode())

# no wrap format(decode)
line21 = sys.stdin.buffer.readline()
print(base64.b64decode(line21).decode())

# MIME format(decode)
line22 = b''
while True:
    tmp = sys.stdin.buffer.readline()
    if tmp == b'':
        break
    line22 = line22 + tmp
print(base64.b64decode(line22).decode())

Ruby

require 'base64'

line1 = STDIN.gets
line1 = line1[0, line1.length() - 1]
res1 = Base64.strict_encode64(line1)

# MIME format(encode)
i = 0
while i < res1.length()
    print res1[i, 76],"\r\n"
    i += 76
end

# no wrap format(encode)
print res1,"\n"

# no wrap format(decode)
line21 = STDIN.gets
line21 = line21[0, line21.length() - 1]
print Base64.strict_decode64(line21),"\n"

# MIME format(decode)
line22 = ""
while tmp = STDIN.gets
    line22 = line22 + tmp
end
print Base64.decode64(line22),"\n"

Perl

use MIME::Base64;

my $line1 = readline(STDIN);
chomp($line1);

# MIME format(encode)
print encode_base64($line1, "\r\n");

# no wrap format(encode)
print encode_base64($line1, ""),"\n";

# no wrap format(decode)
my $line21 = readline(STDIN);
chomp($line21);
print decode_base64($line21),"\n";

# MIME format(decode)
my $line22 = "";
while (my $tmp = readline(STDIN)) {
    $line22 = $line22 . $tmp;
}
print decode_base64($line22),"\n";

Go

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "encoding/base64"
)

func ReadLine(reader *bufio.Reader) (bytes []byte, err error) {
    prefix := false
    buf := make([]byte, 0, 1024)
    line := make([]byte, 0, 1)
    for {
        line, prefix, err = reader.ReadLine()
        if err == io.EOF {
            return
        }
        buf = append(buf, line...)
        if prefix {
            continue
        }
        bytes = buf
        return
    }
}

func main() {
    stdin := bufio.NewReader(os.Stdin)

    line1, err := ReadLine(stdin)
    if err == io.EOF {
        panic(err)
    }
    len1 := base64.StdEncoding.EncodedLen(len(line1))
    res1 := make([]byte, len1);
    base64.StdEncoding.Encode(res1, line1)

    // MIME format(encode)
    for i := 0; i < len(res1); i += 76 {
        end := i + 76
        if end > len(res1) {
            end = len(res1)
        }
        fmt.Println(string(res1[i:end]) + "\r")
    }

    // no wrap format(encode)
    fmt.Println(string(res1))

    // no wrap format(decode)
    line21, err := ReadLine(stdin)
    if err == io.EOF {
        panic(err)
    }
    // base64.StdEncoding.DecodedLenが返す値はあくまで最大値なので、
    // パディングがあったりすると、それよりも短くなることがあるので、
    // Decodeしたときの返り値で実際の長さを取得して切り詰めてやる必要がある。
    len21 := base64.StdEncoding.DecodedLen(len(line21))
    res21 := make([]byte, len21)
    resLen21, _ := base64.StdEncoding.Decode(res21, line21)
    fmt.Println(string(res21[0:resLen21]))

    // MIME format(decode)
    line22 := make([]byte, 0)
    for {
        tmp, err := ReadLine(stdin)
        if err == io.EOF {
            break
        }
        line22 = append(line22, tmp...)
    }
    len22 := base64.StdEncoding.DecodedLen(len(line22))
    res22 := make([]byte, len22)
    resLen22, _ := base64.StdEncoding.Decode(res22, line22)
    fmt.Println(string(res22[0:resLen22]))
}

bash

#! /bin/bash

IFS= read -r line1

# MIME format(encode)
#     base64コマンドは"\n"を付加するので、sedで"\r\n"に変えてやる
echo -n "${line1}" | base64 | sed -e 's/$/\r/'

# no wrap format(encode)
echo -n "${line1}" | base64 -w 0
echo

# no wrap format(decode)
IFS= read -r line21
echo -n "${line21}" | base64 -d
echo

# MIME format(decode)
while IFS=$'\r' read -r tmp ; do
    line22="${line22}${tmp}"
done
echo -n "${line22}" | base64 -d
echo
  • man base64