エンコード・デコード

■ URLエンコード

◆ URLエンコード

URLエンコードは、HTTP のフォーム送信などに用いられるエンコード形式で、CGI にフォームデータを渡す場合などに使用されます。例えば下記のようにエンコードされます。

    NAME=ABC日本語    →    NAME=ABC%93%FA%96%7B%8C%EA
◆ URLエンコードの規則

空白文字を + に置換し、日本語や記号などの特殊文字を %nn 形式(nn は 16進数の文字コード)にエンコードします。エンコードされる記号は下記のものです。

( ) { } [ ] < > ! # $ % & " ' ` ^ ~ = \ | ; : + / ? , 

下記の記号はエンコードされません。

- @ * . _
◆ URLエンコード・デコードサブルーチン

URLエンコード形式のエンコード・デコードを行うサブルーチンを以下に示します。(プログラムはシフトJISであることを想定しています。)

urlencode.pl
print UrlEncode("ABC日本語") . "\n";
print UrlDecode("ABC%93%FA%96%7B%8C%EA") . "\n";

# URLエンコード
sub UrlEncode {
    local($str) = @_;
    $str =~ s/([^ 0-9a-zA-Z\-@\*\._])/sprintf("%%%02X", ord($1))/eg;
    $str =~ s/ /+/g;
    return $str;
}

# URLデコード
sub UrlDecode {
    local($str) = @_;
    $str =~ s/\+/ /g;
    $str =~ s/%([0-9A-Fa-f]{2})/pack("C", hex($1))/eg;
    return $str;
}

■ BASE64エンコードする

◆ BASE64エンコードとは

BASE64 は、バイナリファイルをメールに添付する際などによく用いられます。バイナリデータや日本語データを ASCIIテキストとして扱えるようになるのが特徴です。例えば、下記のようなエンコードが行われます。

This is a pen.    →    VGhpcyBpcyBhIHBlbi4=
◆ BASE64のエンコード規則

入力データの 3バイト 24ビットを 6ビット×4ブロックに分けます。6ビットは 0~63 の値を表しますが、0→A、1→B、2→C のように A~Z、a~z、0~9、+、/ の 64文字に割り当てていきます。入力データが 3バイトの倍数でない場合は、0 埋めでエンコードし、足りないバイト数分、末尾を = で置き換えます。

xxx → 01111000   01111000   01111000
    → 011110  000111  100001  111000 → eHh4
xxx → eHh4       xx → eHg=        x → eA==
◆ MIME::Base64を用いた変換

MIME::Base64 モジュールを利用できる環境であれば、encode_base64()decode_base64() を使用することができます。

base64a.pl
use MIME::Base64;

$encstr = encode_base64("Hello");
print "[$encstr]\n";
$decstr = decode_base64($encstr);
print "[$decstr]\n";

encode_base64() でエンコードされたデータは、76文字毎に "\n" で区切られ、最後にも "\n" が付加されます。"\n" で区切りたくない場合は第2引数(区切り文字)に "" を指定してください。

$encstr = encode_base64("Hello", "");
◆ BASE64エンコード・デコードサブルーチン

MIME:Base64 モジュールが使用できない環境のために、BASE64 エンコード・デコードを行うサブルーチンを下記に示します。

# BASE64エンコードする
#   $encoded_string = Base64Encode($str)
sub Base64Encode {
    local($buf) = @_;
    local($mode, $tmp, $ret);
    local($b64) = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                . "abcdefghijklmnopqrstuvwxyz"
                . "0123456789+/";

    $mode = length($buf) % 3;
    if ($mode == 1) { $buf .= "\0\0"; }
    if ($mode == 2) { $buf .= "\0"; }
    $buf =~ s/(...)/{
        $tmp = unpack("B*", $1);
        $tmp =~ s|(......)|substr($b64, ord(pack("B*", "00$1")), 1)|eg;
        $ret .= $tmp;
    }/eg;
    if ($mode == 1) { $ret =~ s/..$/==/; }
    if ($mode == 2) { $ret =~ s/.$/=/; }
    return $ret;
}

# BASE64デコードする
#   $str = Base64Decode($encoded_string)
sub Base64Decode {
    local($buf) = @_;
    local($mode, $tmp, $ret);
    local($b64) = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                . "abcdefghijklmnopqrstuvwxyz"
                . "0123456789+/";

    $mode = ($buf =~ tr/=/A/);
    $buf =~ s/(.)(.)(.)(.)/{
        $tmp = sprintf("%06b%06b%06b%06b",
            index($b64, $1), index($b64, $2),
            index($b64, $3), index($b64, $4));
        $tmp =~ s|(........)|$ret .= pack("B8", $1)|eg;
    }/eg;
    return substr($ret, 0, length($ret) - $mode);
}

■ gzip形式に圧縮する(Compress:Zlib)

◆ gzip圧縮

Compress::Zlib モジュールを用いて、データを gzip 形式に圧縮することができます。

zlib.pl
use Compress::Zlib;

$infile = "xx2";    # 圧縮前の入力ファイル
$outfile = "xx2.gz";    # 圧縮後の出力ファイル

# 入力ファイルからデータを読み込む
open(IN, $infile) || die "Can't open $infile.\n";
binmode(IN);
read(IN, $indata, (lstat(IN))[7]) || die "Can't read $infile\n";
close(IN);

# 出力ファイルに書き込む
open(OUT, "> $outfile") || die "Can't open $outfile.\n";
binmode(OUT);
print OUT Compress::Zlib::memGzip($indata);
close(OUT);
◆ gzipによるHTTP通信の高速化

ブラウザがサポートしていれば、CGI がブラウザに送りかえすデータを gzip 形式に圧縮して転送することもできます。HTTP ヘッダ部に Content-Encoding で gzip を指定します。

use Compress::Zlib;

# 入力ファイルからデータを読み込む
open(IN, "file.txt");
binmode(IN);
read(IN, $indata, (lstat(IN))[7]);
close(IN);

# CGIの結果として書き出す
binmode(STDOUT);
print "Content-Type: text/html\r\n";
print "Content-Encoding: gzip\r\n";
print "\r\n";
print Compress::Zlib::memGzip($indata);

Copyright (C) 2002 杜甫々