***HPクリエイターのためのCGI講座 第38回***
2.4 パラメータの取得−2

複数のパラメータの取得方法と、文字のデコードについて学びます。

HPのソースコードを覗いてください。
次に示すHTML文の記述があるでしょう。
<FORM action="tst_para.cgi"><INPUT size="20" type="text" name="get_txt1" value="GETの実験−1" maxlength="20"><BR>
<INPUT size="20" type="text" name="get_txt2" value="GETの実験−2" maxlength="20"><INPUT type="submit" value="実行"></FORM>


前項と違って、FORMタグにはさまれて、type="text" のINPUTフォームが2箇所ありますね。
(HPには、2つの入力欄があるので当然ですよね)
このように2つの入力を持った場合、CGIプログラムにはどのようにパラメータが渡されるのでしょうか?
実行結果を見てください。
GETの実験結果は、入力文字を変えていなければ、次のように表示しているはずです。
get_txt1=GET%82%CC%8E%C0%8C%B1%81%7C%82P&get_txt2=GET%82%CC%8E%C0%8C%B1%81%7C%82Q

これから想像できると思いますが、パラメータは '&' で区切られて渡されます。
上記の場合
get_txt1=GET%82%CC%8E%C0%8C%B1%81%7C%82P
get_txt2=GET%82%CC%8E%C0%8C%B1%81%7C%82Q
の2つのパラメータが渡されたことになります。
%で始まるコードは16進数を表しています。

このルールから、HP上のどこの入力領域に、どのような文字列が入力されたかを、判断
します。
method="POST"の場合も標準入力に同じフォーマットでパラメータが渡されます。

前項にならって、下記コードをデコードしてみましょう
get_txt1=GET%82%CC%8E%C0%8C%B1%81%7C%82P

取得したパラメータ get_txt1=GET %82%CC %8E%C0 8C%B1 %8C%7C %82P
文字コード種 ASCIIコード シフトJISコード シフトJISコード シフトJISコード シフトJISコード シフトJISコード
実際の文字 post_txt=POST

日本語の'1'のコードは0x8250です。
ではなぜ、入力文字列は、%82%50(%82Pになっている)にならないでしょう?
%50はASCIIコードの'P'です。
ASCIIコードに存在する文字コードは、16進変換しません。
したがって、日本語の'1'は %82P として表されます。

<例題プログラムの解説>
例題プログラムでパラメータの振り分けと、文字のデコードをしています。
順次説明します。
このCGIプログラムは、method="GET" で起動される事を前提で作られています。

$buffer = $ENV{'QUERY_STRING'};
環境変数 $ENV{'QUERY_STRING'} から、$bufferへパラメータの取り出しています。

@para = split(/&/,$buffer);
パラメータは '&' で区切られて入ってくる事は説明しましたね。
split演算子で、'&' を区切り記号として、パラメータを分解し、配列 @para へ格納しています。
(split演算子は「Perl入門 5.3 その他」で解説)
結果として、$para[0] には最初のパラメータが、$para[1]には2番目のパラメータが設定されます。
例題の入力文字列(パラメータ)を変更していなければ
$para[0] = 'get_txt1=GET%82%CC%8E%C0%8C%B1%81%7C%82P'
$para[1] = 'get_txt2=GET%82%CC%8E%C0%8C%B1%81%7C%82Q'
が設定されています。

($name1, $value1) = split(/=/, $para[0]);
($name2, $value2) = split(/=/, $para[1]);
パラメータは、「名前=値」のフォーマットで入ってきます。
ここでも、split演算子で、'='を区切り記号にして、名前と値に分解します。
それぞれ、$name1, $value1と$name2, $value2に「名前」と「値」が設定されます。
例題の入力文字列を変更していなければ
$name1 = 'get_txt1'
$value1 = 'GET%82%CC%8E%C0%8C%B1%81%7C%82P'
$name2 = 'get_txt2'
$value2 = 'GET%82%CC%8E%C0%8C%B1%81%7C%82Q'
が設定されています。

$value1 =~ tr/+/ /;
変換規則に従い、tr演算子を使って'+'をブランクに変換します。
(tr演算子は「Perl入門 5.3 その他」で解説)
入力文字列を変更していなければ、この命令は、なにも行いません。
(ブランク文字の入力がないので)

$value1 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
s演算子で16進コード文字列を数値に変換します。
(s演算子は「Perl入門 5.2 文字列置換」で解説)
16進コードを正規表現で表せば、/%[a-fA-F0-9][a-fA-F0-9]/ですね。
(例題プログラムと違いカッコを使っていないことに注意!)
理由:
16進コードのフォーマットは次の通りである。
1文字目='%'
2文字目='0'〜'9'もしくは、'A'〜'F'もしくは、'a'〜'f'(小文字もOK)
3文字目='0'〜'9'もしくは、'A'〜'F'もしくは、'a'〜'f'(小文字もOK)
これを正規表現で表すと %[a-fA-F0-9][a-fA-F0-9] になる。

プログラム例ではカッコが付いて %([a-fA-F0-9][a-fA-F0-9]) となっていますね。
このカッコは、一致したパターンを記憶する機能があります。
記憶したパターンは、$1 の名前を持つシステム変数に記憶します。
例えば %A5 の文字列を見つけると A5 を$1に格納します。


16進コードとして認識された文字パターンは、pack関数で1文字(1byte)に変換します。

関数とは
サブルーチンと呼ばれる場合もあります(厳密に言うと違いますが)。
他プログラムから呼び出すことのできるプログラムのことです。
Perlではあらかじめ、便利な機能を持つプログラムが多数用意されていて、 ユーザーは自由に呼び出すことができます。
このように、あらかじめ用意されているプログラムを特に組込み関数(サブルーチン)と呼びます。

<s演算子のパラメータ eg について>
g はすでに説明済みですね。
文字列全体を変換対象にする、と言う意味でしたね。
e は初めて出てきました。
e は右のオペランドを実行せよ、と言う意味です。
もし、e を指定せず次のように指定すればどのような変換が行われるでしょうか?
$value1 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/g;
16進コードは、'pack("C", hex($1))'の文字列に変換されてしまいます。
pack("C", hex($1)) は文字列ではなく、関数として実行させたいので、e を指定してい
ます。

=例=
s/X/1+3/g; # Xは 1+3 の3文字に変換される
s/X/1+3/eg; # Xは 4 に変換される


<組み込み関数の説明>
hex():
16進コードを10進数に変換します
=例=
$a='A5';
$b=hex($a); # $bには165が設定される

pack():
数値を1文字(1byte)に変換します
pack("C", 50); # 50を1文字(1byte)に変換

プログラム例の pack("C", hex($1)) を説明すると、一致した16進コード( $1 )をhex関数で10進数に変換して、その10進数をpack関数で1文字に変換しています。

pack関数の使い方を、本当に理解しようとすると、コンピュータのアーキティクチャについて、知る必要があります。
本講座では、そこまで突っ込んだ講習は行いません。
この部分については、CGIのパラメータ取得のためのおまじない、のように覚えてしまってください。

<パラメータ取得のおまじない>
# $buffer にはパラメータがコピーされているものとする
@para = split(/&/,$buffer);
foreach (@para) {
    ($name, $value) = split(/=/);
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    #
    # $name, $valueは次のループで壊れるので、ここで別の変数にコピー
    # 必要であれば他の処理も入れる
    #
}


********************************************************************************
講師:ALK alk@arkland.co.jp
運営:アークランド(株) http://www.arkland.co.jp/