Perl/CGIプログラムの入力処理(1)

今回は、入力フォームから送られてきたデータを、Perl/CGIプログラムで受け取る方法について学習します。

何らかの入力データを、Perl/CGIプログラム側で受け取る処理は、あたりまえすぎてすでに定番化されています。

モジュールやサブルーチンなど、参考になるPerl/CGIプログラムはたくさんあります。

なので、今回は学習というよりも、読み物として楽しんでいただければと思います。

  1. 編集前記
  2. 基礎知識
  3. 入力処理
  4. まとめ
  5. 編集後記

編集前記

今回は基礎的な部分を学習していくということで、見ていただければわかるように、かなりの量になってしまったので早速 Perl/CGIプログラム学習 をはじめていきたいと思います。

基礎知識

まずは基礎的なところからということで、インターネット上でデータをやり取りする際に、登場するであろう用語の意味から学習していきましょう。

リクエストとレスポンス

リクエストとは、インターネットエクスプローラなどのウェブブラウザ(クライアント)から、インターネットサーバー上の特定のファイルに対して、何らかの反応を求める行為のことです。

リクエストというとわかりにくいですが、ウェブブラウザなどのクライアントから何らかの要求、または命令を送る好意全般だと考えていただければわかりやすいかと思います。

リクエスト先はHTMLファイルであろうと、CGIファイルであろうと関係ありません。

なのであなたがウェブページを表示させるためにURLを入力したり、リンクをクリックする行為もリクエストに当たります。

あなたが送ったリクエストに対して、ウェブサーバーから何らかのデータが帰ってくることをレスポンスといいます。

これはウェブページであろうと、ウェブページが存在しないという404のエラーだろうと、返ってくる反応は全てレスポンスとなります。

そして Perl/CGIプログラム では、このレスポンスをリクエスト時の状況により自在に変化させることができます。

ここまでPerl/CGIプログラミングを学習してきたあなたであれば、このあたりは想像つくと思います。

例えば、Perl/CGIプログラムがリクエストされた日付や時間により、コンテンツを表示させたりさせなかったりとか…

環境変数 によりクライアントの端末をある程度絞ることができるので、パソコンと携帯で別のページを表示させたりなどですね。

このように使い方はいろいろありますが、最も多く利用されているのは、パラメータによりレスポンスの内容を変化させるという手法です。

パラメータ

パラメータとは一言で言うと、クライアントつまりウェブブラウザから、インターネットサーバーのCGIにアクセスした際わたされるデータ一式のことです。

パラメータを受け取ったCGIプログラムは、パラメータの内容を判断し、ウェブブラウザにレスポンスを送ります。

つまりパラメータとは、インターネットサーバーに設置されているCGIプログラムの動きを指定している外部入力設定のようなものです。

以上これはクライアント側から見た説明です。

なので、われわれPerl/CGIプログラマー側から見れば、インターネットエクスプローラなどのクライアントからパラメータを受け取り、処理しなくてはいけないときもあるということです。

そしてここからが本題なのですが…。

ウェブブラウザなどのクライアントが送るリクエストには、GETリクエストと、POSTリクエストの2種類があります。

リクエストといわずに、送信という場合もあります。

当然Perl/CGIプログラム側からパラメータを受け取るときも、GET送信かPOST送信で送られてきたデータを受け取ることになります。

なので、Perl/CGIプログラマーに限らず、インターネット関連のプログラミングを行う上で、GET送信とPOST送信の違いを理解しておくことはとても大切です。

GET送信とPOST送信

インターネットエクスプローラなどのウェブブラウザ(クライアント)からCGIへリクエストする際には、GET送信とPOST送信の2種類の送信方式があります。

ここではGET送信と、POST送信について簡単に説明します。

GET送信

GET送信とは、URLはもちろん、CGIに渡すデータ(パラメータ)まですべての値がオープンになった上で行われる送信方法のことです。

↑いろいろ細かいことを犠牲にしつつ一言で説明するとこうなります。

GET送信でCGIにリクエストを送るにはいくつかの方法があります。

1、インターネットエクスプローラなどのウェブブラウザのアドレスバーに、直接CGIファイルまでのURLを指定する。
2、 HTMLのリンクタグ でCGIファイルまでのパスを直接指定する。
3、 HTMLのフォームタグ で、methodをGETにして、入力データを送信する。

つまり、あなたがいつも何気なくやっているインターネットエクスプローラなどのウェブブラウザのアドレスバーに直接URLを指定するという行為。

何かを調べるために、「http://google.co.jp」などとアドレスバーに入力していませんか?

それと HTMLのリンクタグ でウェブページを指定する場合。

「<a href="index.html">トップページ</a>」などとしますよね。

これらはすべて、パラメータがないだけで基本的にはGET送信になります。

GET送信でパラメータを指定するときは、CGIファイルまでのURLを指定した後に続けて、パラメータを記述していきます。

GET送信の利点は、とにかくパラメータも含め、パスが全て見えているところにあります。

パラメータも含めたパスが全て見えていると何がどう有利なのかというと、外部からのアクセスでも一発でこちらの希望するレスポンスを得ることができるというところです。

例えば、Googleで「Perl CGI 入門」と検索すると以下のページが表示されます。

http://www.google.co.jp/search?hl=ja&source=hp&q=Perl+CGI+%E5%85%A5%E9%96%80&lr=&aq=f&aqi=g1g-m1&aql=&oq=&gs_rfai=

このように、GET送信だと検索結果ページをダイレクトにリンクさせることができるのです。

ちなみに「?」以降がパラメータになります。

逆にGET送信の欠点は、送信できるデータの量に限りがあるので、パラメータの量が多いときには使えないというところです。

POST送信

POST送信とは、インターネットエクスプローラなどのウェブブラウザがバックグラウンドでデータを送信する方法のことです。

いろんな細かいことを犠牲にして、一言で説明するならこのようになります。

POST送信でデータを送るには、 HTMLのフォームタグ で、methodをPOSTにして、入力データを送信するしかありません。

まぁ、別サーバーのCGIやAJAX(Javascript)などからPOST送信することもできますが、ここではそんなケースは無視します。

前述のGET送信ではパラメータ付きURLを直接指定するのに対し、POST送信ではバックグラウンドで処理するためパラメータは見えません。

その代わり、POST送信の方がGET送信よりもたくさんのデータを送信することができます。

なので、大量の入力情報を扱うときや、前述の検索結果ページのように、外部から直接リクエストしてほしくないときなどに使います。

GET送信と、POST送信の違いは、なんとなくでも理解しておいてくださいね。

Perlプログラムを作成していく上では、必須の知識だともいえますので。

入力処理

それでは、GET送信またはPOST送信のデータを受け取る Perl/CGIプログラムを作成する ための学習に移りましょう。

GET送信フォーム

HTMLのフォームタグ で、GET送信するためには、formタグのmethodでGETを指定する必要があります。

<form action="~" method="GET">
<input type="submit" value="OK" />
</form>

「~」には、Perl/CGIプログラムまでのパスが入ります。

これは、「OK」とかかれたボタンを表示するフォームタグ一式です。

「OK」ボタンを押すと、GET送信でPerl/CGIプログラムにデータが送られます。

ただし、GET送信ではパラメータをPerl/CGIプログラムに渡せるデータの量が限られているので、フォームタグで入力されたデータを送るときには注意が必要です。

例えば、前述のような検索キーワードのみの送信とか、メールアドレスのみの入力などパラメータの量も少ないことがわかっているときですね。

GET送信データを受け取る

GET送信で送られてきたパラメータを、 Perl/CGIプログラム 側で受け取るには、 環境変数「$ENV{'QUERY_STRING'}」 を参照します。

$ENV{'QUERY_STRING'}には、GET送信で送られてきたパラメータが格納されています。

そしてパラメータ内は、一定の規則に従ってデータが並べられています。

なので、一度別の変数に代入して、その後にPerl/CGIプログラム内で使いやすいように整形しなおすのが常套手段です。

POST送信フォーム

POST送信を実現するためには、 HTMLフォームタグ のMETHODでPOSTを指定します。

<form action="~" method="POST">
<input type="submit" value="OK" />
</form>

「~」には、Perl/CGIプログラムまでのパスが入ります。

これは、「OK」とかかれたボタンを表示するフォームタグ一式です。

先ほどのGET送信用のフォームタグと同じものです。

違う部分は、method指定がGETではなく、POSTになっているというところです。

「OK」ボタンを押すと、POST送信でPerl/CGIにデータが送られます。

POST送信データを受け取る

POST送信で送られたパラメータを Perl/CGIプログラム 側で受け取るには、標準入力コマンドを使用します。

ここで言う標準入力コマンドとは、大雑把に言ってしまうと、キーボードからの入力データを格納しているメモリからの読み取りを促す命令のことです。

POST送信の方がGET送信よりも大量のデータを扱うことになるので、GET送信を受け取る要領で単純に環境変数を参照すれば済むという話ではないのです。

Perl/CGIプログラム側から標準入力コマンドを使って、POST送信形式のパラメータを取得するにはread関数を使用します。

read関数とは、読んで字のごとく何かを読み取る関数です。

read関数の使用方法については、ある程度フォーマットのようなものが決まっています。

read関数を使用するには、read関数を指定した後に、決められた順番で引数を指定していきます。

read(FILEHANDLE, scalar, length, offset)

read自体は、ファイルハンドルから何らかのデータを読み取る関数です。

第1引数のファイルハンドル(FILEHANDLE)から、第2引数の 変数 (scalar)に、第3引数のバイト(length)数分のデータを読み込みます。

第4引数のOFFSETを指定すると、指定した位置から読み込みを行なうことができますが、今回のようにPOST送信されたパラメータをまとめて受け取りたいときなどは、省略できます。

パラメータ

パラメータ内は一定の規則によりデータが並んでいます。

GET送信でもPOST送信でも同じ規則でデータが並んでいます。

なのでPerl/CGIプログラム側でGET送信またはPOST送信データを受け取った後は、同じ方法でデータを整形していけばよいことになります。

それではパラメータ内部を見ていきましょう。

例えば、以下のような HTML入力フォーム から Perl/CGIプログラム にデータが送られるとしましょう。

<form action="mail.cgi" method="get">
<input type="hidden" name="mode" value="addition" />
<input type="text" name="email" size="30" />
<input type="submit" value="OK" />
</form>

メールアドレスを入力しOKボタンを押すと、Perl/CGIプログラムにGET送信が行われるフォームです。

パラメータは、「キー=値」というペアでまとめられて送信されます。

キーというのは、formタグのinputで指定されているnameの文字列をさしています。

値というのは、formタグのinputで指定されているvalueの文字列または、入力された文字列をさしています。

上記のformの場合は…

<input type="hidden" name="mode" value="addition" />

この部分でキー「mode」と値「addition」のペアが作成されます。

「mode=addition」ですね。

<input type="text" name="email" size="30" />

この部分で、キー「email」に対応する値として、入力されたメールアドレスがセットされます。

「email=入力されたメールアドレス」ですね。

以上のようになるわけなのですが…

今回のようにキーと値のペアが複数あるときは、「&」でひたすらつなげられていきます。

つまり、「mode=addition&email=入力されたメールアドレス」という感じですね。

そしてGET送信の場合はPerl/CGIプログラムファイルの後に、「?」が付加されパラメータが続きます。

なので「mail.cgi?mode=addition&email=入力されたメールアドレス」となるわけです。

これがパラメータ書式の概要です。

しかし、この書式でパラメータを作成した場合、ひとつ困ったことが起こることにお気づきでしょうか?

それは、パラメータの区切り文字として使われている「=」「&」が入力文字列内にあった場合、区切り文字と区別がつかなくなることです。

さらにそれだけではなく、実は日本語文字を送信するときにも困ります。

なので、何がくるかわからない入力文字列に関しては、URLエンコードしなくてはいけない規則になっています。

URLエンコード

インターネットエクスプローラなどのウェブブラウザからPerl/CGIプログラムにデータを送信する場合…。

送信するデータがアルファベットや数字だけであれば問題ないのですが、記号や日本語などの文字を送信する場合には、URLエンコードをしなければいけないルールになっています。

なのでウェブブラウザは、フォームの値をサーバに送信する際に自動的にURLエンコードをおこなっているのです。

基本的に半角英数字についてはURLエンコードする必要はありませんが、それ以外の文字についてはURLエンコードしなくてはいけません。

URLエンコードとは、まず半角スペースを「+」に変換し、その他の文字を「%HH」形式の 16進数 に置き換える処理のことです。

複数のバイト数を持った文字でも関係ありません。

ひたすら1バイトずつ取り出し、それを16進数に置き換えていくのです。

そして、頭に「%」をつければ1バイト分ができあがります。

これをすべてのバイトに摘要してつなぎ合わせればURLエンコードの完成です。

各文字コードの違いを考慮する

それでは実際に文字をURLエンコードしてみましょう。

前述のURLエンコードについての説明を読んでいただければわかるように、URLエンコード後の16進数というのは、文字に割り振られているコードが元になっています。

そしてその文字に割り振られているコード群のことを文字コードといいます。

ということは、文字コードによってURLエンコード後の値は異なるわけです。

日本で主に利用されている文字コードは、Shift_jisコード、EUCコードやUTF-8コードなどがあります。

たとえ同じ文字でも各文字コードの違いによって、 URLエンコード後の値が異なることを、あなたにもわかりやすく理解していただくためにこんなページ を作ってみました。

この URLエンコードの解説ページ には、指定した文字コードの文字をURLエンコードしてくれるツールを公開しています。

使い方も簡単で、入力欄に何らかの文字を入力してCheckボタンを押すと、URLエンコードされた文字が表示されます。

例えば、「あ」と入力しCheckボタンを押すと以下のようになります。

Shift_jisコードでは「%82%A0」。
EUCコードでは「%A4%A2」。
UTF-8コードでは「%E3%81%82」。

同じ「あ」という文字でも使用する文字コードによって、URLエンコード後の結果も違うということが理解できたかと思います。

まとめ

それでは、 HTMLフォーム から入力データを送信し、 Perl/CGI 側でそれを受け取り、プログラム内で使用できるよう整形する一連の処理を作ってみましょう。

HTMLフォームの作成

まずは、Perl/CGIプログラムにリクエストを送るためのフォームを作成します。

<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=shift_jis">
<title>Form Sample</title>
</head>
<body>
<div align="center">
<h1>Form Sample</h1>
<form action="index.cgi" method="get">
<table border="0" cellspacing="5" cellpadding="5">
<tr valign="top" align="left">
<td>NAME</td>
<td><input type="text" name="name" size="30"></td>
</tr>
<tr valign="top" align="left">
<td>EMAIL</td>
<td><input type="text" name="email" size="30"></td>
</tr>
<tr valign="top" align="left">
<td></td>
<td><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</div>
</body>
</html>

これが HTML フォームのサンプルになります。

GET送信フォームなので「get.html」とでも名前を付けておきます。

もしPOST送信フォームにしたい場合は…

<form action="index.cgi" method="get">

という部分の「method="get"」を「method="post"」にします。

これでPOST送信用のフォームが完成します。

それと、文字コードはシフトジスコードになっているので、変更する場合は METAタグ を書き換えます。

EUCコードにしたい場合は…

<META http-equiv="Content-Type" content="text/html; charset=euc-jp">

UTF-8コードにしたい場合は…

<META http-equiv="Content-Type" content="text/html; charset=utf-8">

それぞれこのように変更すればOKです。

自由にカスタマイズしてください。

Perl/CGIプログラムの作成

次は、 Perl/CGIプログラムを作成 します。

#!/usr/bin/perl

use strict;

print "Content-type: text/html\n\n";

if (my $parameter = &GetParameter) {

foreach my $key (keys %{$parameter}) {
print "$key : $parameter->{$key}<br>\n";
}

} else {

print 'No Parameter !';

}
exit;

sub GetParameter {

my $query = "";
my $flag = "";
my %parameter = ();

if ($ENV{'REQUEST_METHOD'} eq 'GET') {
$query = $ENV{'QUERY_STRING'};
} elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
}

foreach (split(/&/, $query)) {
my ($key, $value) = split(/=/);
$value =~ tr/+/\x20/;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$parameter{$key} = $value;
$flag = 1 unless $flag;
}

if ($flag) {
return \%parameter;
} else {
return;
}
}

前述の入力フォームサンプルでindex.cgiを指定しているので、ファイル名はindex.cgiとします。

設置と動作

上記の入力フォームサンプルHTMLと、Perl/CGIスクリプトを同じフォルダ(ディレクトリ)に入れてサーバーにAsciiモードでアップロードします。

Perl/CGIプログラムのパーミッションを、701や755などに変更して実行権を与えたら準備完了です。

まずは、入力フォームサンプルのHTMLファイルではなく、Perl/CGIプログラムに直接アクセスしてみてください。

「No Parameter !」と表示されます。

詳しくは後述しますがこれは、パラメータが何もないつまり、何も入力情報が送られてきていないことを表しています。

では次に、入力フォームサンプルのHTMLファイルにアクセスします。

NameとEmailの入力欄があるので適当に入力しボタンを押します。

すると、先ほどのPerl/CGIプログラムに入力データが送られて…

email : ...
name : ...

このように表示されます。

当然「...」にはそれぞれ入力した文字列がきます。

これは、「キー : 値」というペアで表示されています。

キーとは、formタグのinputで指定されているnameのことです。

値とは、入力された文字列のことです。

いろいろな文字列を入力して試してみてください。

あなたが入力した文字列が、しっかりPerl/CGIプログラムに渡されている様子がわかると思います。

Perl/CGIスクリプト解説

それでは、 Perl/CGIプログラム(スクリプト) について解説していきます。

このPerl/CGIプログラムは、パラメータをチェックします。

何らかの入力があれば、そのキーと値を表示します。

パラメータが何もない場合は「No Parameter !」というメッセージを表示します。

それでは詳しくみていきましょう。

GetParameter関数

GetParameter 関数 はパラメータを取得します。

パラメータを取得した場合は、データを整形し ハッシュ配列 リファレンス として返します。

取得できなかった場合は、戻り値はありません。

sub GetParameter {

my $query = "";
my $flag = "";
my %parameter = ();

if ($ENV{'REQUEST_METHOD'} eq 'GET') {
$query = $ENV{'QUERY_STRING'};
} elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
}

foreach (split(/&/, $query)) {
my ($key, $value) = split(/=/);
$value =~ tr/+/\x20/;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$parameter{$key} = $value;
$flag = 1 unless $flag;
}

if ($flag) {
return \%parameter;
} else {
return;
}
}

この関数はGET送信またはPOST送信に関係なくパラメータを取得することができます。

if ($ENV{'REQUEST_METHOD'} eq 'GET') {
$query = $ENV{'QUERY_STRING'};
} elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
}

まず、 環境変数「$ENV{'REQUEST_METHOD'}」 を参照することにより、GET送信でアクセスされたのかPOST送信でアクセスされたのかを if文を使って判定 します。

GET送信の場合は、環境変数「$ENV{'QUERY_STRING'}」を参照することによりパラメータを取得します。

POST送信の場合は、read関数を使います。

read関数を使用するには、read関数を指定した後に、決められた順番で引数を指定していきます。

read(FILEHANDLE, scalar, length)

readは、 ファイルハンドル から何らかのデータを読み取る関数です。

第1引数のファイルハンドル(FILEHANDLE)から、第2引数の 変数 (scalar)に、第3引数のバイト(length)数分のデータを読み込みます。

つまり、環境変数「$ENV{'CONTENT_LENGTH'}」で指定されているバイト数分のデータを変数「$query」に代入しているわけです。

これで、GET送信またはPOST送信に関係なくパラメータを取得することができました。

次は、取得したパラメータを分解し整形します。

foreach (split(/&/, $query)) {
my ($key, $value) = split(/=/);
$value =~ tr/+/\x20/;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$parameter{$key} = $value;
$flag = 1 unless $flag;
}

パラメータ内は、前述しましたように「キー=値&キー=値」となっています。

なので「&」で区切られているペアの数の分だけ foreach関数で繰り返し処理 をします。

パラメータの区切り文字「&」と「=」を基準に split関数 で各ペアの文字列をキーと値にまで分解します。

値(フォーム入力データ)に関しては前述しましたようにURLエンコード処理が施されているので、URLデコード処理をします。

URLデコードとは、URLエンコードされたデータを元に戻す処理のことです。

$value =~ tr/+/\x20/;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;

「+」を\x20に 変換 します。

\x20とは、半角スペースの 16進コード です。

「%HH」で1バイト分のデータなので、「%」以降の16進数部分を パターンマッチで取り出し hex関数とpack関数でひたすら文字に戻していきます。

これはURLデコードの定番処理なので、メモしておくといろいろと便利です。

あとは、キーと値で連想配列の要素を作成しループ終了です。

最後は、 連想配列のリファレンス を返しています。

もしパラメータを取得できなかった場合は、戻り値はありません。

入力判定

サブルーチン GetParameterはパラメータの有無を示してくれるので、 if分で分岐処理 させています。

if (my $parameter = &GetParameter) {

foreach my $key (keys %{$parameter}) {
print "$key : $parameter->{$key}<br>\n";
}

} else {

print 'No Parameter !';

}
exit;

パラメータを取得した場合つまり、if分が成立したときは ハッシュ配列の要素 を全て表示させています。

スカラ変数 「$parameter」となっていますが、中身はハッシュ配列「%parameter」のリファレンスなので注意しましょう。

パラメータを取得できなかった場合は、else以降が実行されます。

Perl/CGIプログラム解説は以上です。

Perl/CGIプログラミング学習に

Perl/CGIプログラミング学習に役立つコンテンツを紹介します。

モダンPerl入門

Perlは最初のバージョンから10年以上経ち、いまだにさまざまなシステムに使われていますが、残念ながらこの長い期間に蓄積された世界中のPerlハッカー達のノウハウは、彼らの世界でとどまり、あまり世に広まっていません。

こちらで紹介されているのは、初心者向けの「CGIを書くためのPerlの書き方」ではなく、「中級レベルのPerl」「業務に使えるPerl」「システム構築等に使える実用的なPerl」です。

Perl初心者向けのコンテンツに飽きてきた方は、ぜひのぞいてみてください。

編集後記

今回は、Perl/CGIプログラムでHTMLフォームからの入力を受け付ける方法について学習しました。

GET送信またはPOST送信データを受け取り、URLデコードするという処理は、ほとんどのPerl/CGIプログラムで用いられる定番処理です。

インターネット上で配布されているPerl/CGIプログラムなども参考にして、あなた独自の 関数ライブラリを作成 しておくと便利です。

ところで、今回の学習であげたようなURLデコード処理プログラムは、定番化しているものなのでよく見かけますが、逆処理のURLエンコード部分の処理はなかなか見かけませんよね。

URLエンコード処理に関しては、フォーム送信時にウェブブラウザ側で自動的に行われるものです。

当然Perl/CGIプログラム側で処理すべきものではないので、ほとんど見かけないわけです。

でも、せっかくここまでPerl/CGIプログラムの入力処理を学習してきたわけですから、URLエンコード部分もコーディングしてみたくありませんか?

ということで、URLエンコードを行うPerl/CGIプログラムを作ってみました。

#!/usr/bin/perl

use strict;

print "Content-type: text/html\n\n";
print &encode('あ');
exit;

sub encode {
my $string = shift;

$string =~ s/\x20/+/g;
$string =~ s/([^A-Za-z0-9\+])/'%'.uc(unpack("H2", $1))/ego;

return $string;
}

Shift_jisコードで保存し実行すると、「あ」を「%82%A0」に変換します。

ほかの文字でも同様にURLエンコードします。

文字コードは、EUCコードやUTF-8コードでも問題なく動作します。

ただし、UTF8フラグは立てないでください。

今回の学習は以上です。

<戻る>