・GetやPostのパラメータを取得する関数
Perlの場合、少々面倒ですが、自分でパラメータ文字を解析する必要があります。
▼サンプル----------------------------
sub GetPara
{
my($query_string); #// エンコードされたパラメータ全体
my(@a, $a); #// エンコードされたパラメータを分解したもの
my($name, $value); #// デコードされたパラメータ
my(%in); #// ハッシュ配列
#// パラメータの読み込み
if ($ENV{"REQUEST_METHOD"} eq "POST")
{
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{"CONTENT_LENGTH"});
}
else
{
#// GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}
#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);
# パラメータの取得
foreach $a(@a)
{
#// 「変数名1=値1」の形式をイコール(=)で分解
($name, $value) = split(/=/, $a);
#// + や %8A などのデコード
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex( $1 ))/eg;
#// 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{$name} = $value;
}
return %in;
}
▲-----------------------------------
フォームなどから値を送った場合、
・・・/param.cgi?a=1&b=2
のように、URLの後に?が付き、その後がパラメータになります。
関数内で、そのパラメータ部分を取得して、
aの値が1、
bの値が2
という情報に分解し、ハッシュ配列に格納します。
パッケージ化するか、クラス化してしまうと使い回しが効いて、後の開発が楽に
なります。パッケージ化、クラス化については別の機会に。
この関数の使用例は、以下のようになります。
▼フォームサンプル(/form.html)--------
<html>
<body>
<form method="post" action="/cgi-bin/param.cgi">
ハンドルネーム <input type="text" name="handle" size="30"><br>
メールアドレス <input type="text" name="mail" size="30"><br>
<input type="submit" name="submit1" value="送信">
</form>
</body>
</html>
▲------------------------------------
▼CGIサンプル(/cgi-bin/param.cgi)-----
#!/usr/local/bin/perl
use strict;
my %param;
%param = &GetPara();
print "Content-type: text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
END_OF_HTML
print "ハンドルネーム : $param{'handle'}<br>";
print "メールアドレス : $param{'mail'}<br>";
print << "END_OF_HTML";
</body>
</html>
END_OF_HTML
(GetPara関数をここに挿入)
exit(0);
▲------------------------------------
▼出力結果----------------------------
ハンドルネーム : しょう
メールアドレス : test@test.jp
(ハンドルネーム : しょう
メールアドレス : test@test.jp
と入力した場合)
▲------------------------------------
この場合、パラメータは、
handle=%82%B5%82%E5%82%A4&mail=test@test.jp&submit1=%91%97%90M
となります。GETで送信すれば、ブラウザのアドレスバーで確認できます。
日本語の部分は、URLエンコードされた文字コードになっていますが、CGIで取得した
時には、日本語に戻ります。
・暗号化、復号化関数
Perlでは、復号化できない暗号化関数は用意されていますが、復号化できる
暗号化関数・復号化関数は用意されていません。ですので、自分で作成しなければ
なりません。
復号化できない暗号化関数は、cryptという関数でできます。こちらは、パスワード
を暗号化して保存して、入力されたパスワードを同じく暗号化して、チェックする時
などに使用します。
復号化が必要な場合というのは、例えばメールアドレスなどを暗号化し、クッキーに
保存した後、そのメールアドレスを復号化して別のページに表示するなどが考えられ
ます。
以下に暗号化関数と、復号化関数のサンプルを記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : StringEncode
# 概要 : 暗号化関数
# 引数 : comment 暗号化する文字列
# : key エンコード用パスワード
# 戻値 : エンコードされた文字列
#------------------------------------------------------------------
sub StringEncode
{
my($comment, $key, $i, $j, @key, %pcp_table);
$comment = $_[0];
$key = $_[1];
$comment .= "\0" if (length($comment) % 2);
@key = split(//, $key);
$i = 0;
$j = MakeTable($key, \%pcp_table);
$comment =~ s/./sprintf("%03o",ord($&)^(ord($key[$i++ % @key])+($j++ % 383)))/ges;
$comment =~ s/../$pcp_table{$&}/g;
return $comment;
}
#------------------------------------------------------------------
# 関数 : StringDecode
# 概要 : 復号化関数
# 引数 : comment デコードする文字列
# : key エンコード用パスワード
# 戻値 : デコードされた文字列
#------------------------------------------------------------------
sub StringDecode
{
my($comment, $key, $i, $j, @key, %pcp_table);
$comment = $_[0];
$key = $_[1];
@key = split(//, $key);
$i = 0;
$j = MakeTable($key, \%pcp_table);
$comment =~ s/./$pcp_table{$&}/g;
$comment =~ s/.../sprintf("%c",oct($&)^(ord($key[$i++ % @key]) +($j++ % 383)))/ges;
$comment =~ s/\0$//;
return $comment;
}
#------------------------------------------------------------------
# 関数 : MakeTable
# 概要 : 変換テーブルの作成
# 引数 : 変換する文字列、変換テーブル
# 戻値 : 変換後の文字列
#------------------------------------------------------------------
sub MakeTable
{
}
▲-----------------------------------
中身の説明は難しくなりますので、簡単に。(^_^)
エンコード(暗号化)関数は、第1引数に暗号化する文字列を、第2引数に暗号化の
キーとなる文字列を渡します。このキーは任意の文字列ですが、デコード(復号化)
関数の第2引数も同じ文字列にする必要があります。
エンコード(暗号化)関数の中で、MakeTable関数という文字列の変換テーブルを
呼び出し、第2引数で渡されたキーで暗号化を行います。
同じく、デコード(復号化)関数の中で、MakeTable関数を呼び出し、第2引数で
渡されたキーで復号化します。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
my($str, $str1, $str2);
$str = "test";
$str1 = StringEncode($str, "8zHCLz9h");
$str2 = StringDecode($str1, "8zHCLz9h");
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
暗号前 str = $str<br>
暗号後 str1 = $str1<br>
復号後 str2 = $str2<br>
</body>
</html>
END_OF_HTML
(StringDecode関数、StringEncode関数、MakeTable関数をここに挿入)
exit(0);
▲------------------------------------
▼出力結果----------------------------
暗号前 str = test
暗号後 str1 = aO!hcH
復号後 str2 = test
▲------------------------------------
暗号化前後と復号化した結果が分かっていただけたかと思います。
この処理を使えば、ユーザの個人情報は、暗号化してファイルやデータベースに
保存しておき、そのユーザ本人だけに入力した情報を見せたりすることができる
ようになります。
・ファイルの排他制御
排他制御とは、複数のユーザが同時にページにアクセスした場合に、同時にファイル
の書き込みを行うことを防ぐ制御のことです。
排他制御を行わないと、いろいろな障害が発生します。例えば、アクセスカウンター
を、ファイル保存で行っている場合、
Aさんがアクセスし、ファイルを読み込み、カウント10を取得
Bさんがアクセスし、ファイルを読み込み、カウント10を取得
Aさんの処理でカウントを増やし、カウント11を保存
Bさんの処理でカウントを増やし、カウント11を保存
AさんとBさんの2人がアクセスしたのに、カウントは10から11しか上がりません。
排他制御をしていないと、このような現象が起きます。
また、ひどいときには、ファイルが破壊されてしまうことがあります。
Aさんがアクセスし、ファイルを読み込み、カウント10を取得
Aさんの処理でファイル書き込みモードでオープン(一旦クリア)
Bさんがアクセスし、ファイルを読み込み、空のデータを取得
Aさんの処理でカウントを増やし、カウント11を保存
Bさんの処理でカウントを増やし、カウント1を保存
アクセスカウント程度ならまだ良いですが、大事なデータを扱っている場合などは、
このようなことが起きると大問題になります。
そこで、排他制御が必要になります。
以下にトランザクション開始関数と、トランザクション終了関数のサンプルを記述
します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : StartTransaction
# 概要 : ファイルトランザクションの開始
# 10秒間ロックがはずれない場合は、ロックを解除して失敗を返す
# 引数 : 対象データファイル名
# 戻値 : テンポラリファイル名(ロック失敗の場合は空文字)
#------------------------------------------------------------------
sub StartTransaction
{
my $datafile = $_[0];
my $tmpfile = $datafile;
$tmpfile =~ s/(\w+)\.\w+$/$1\.tmp/i;
#// ロックがかかっている場合は10秒待つ
foreach(1 .. 10)
{
#// テンポラリファイルが存在するかを確認
unless(-f $tmpfile)
{
#// ロックされていなければテンポラリファイルを作成
if (open(TMP, ">$tmpfile"))
{
print TMP "$datafile TMP File\n";
close(TMP);
return $tmpfile;
}
}
#// ロック中の場合は1秒待つ
sleep(1);
}
#// ロックを強制的に解除
EndTransaction($tmpfile);
return "";
}
#------------------------------------------------------------------
# 関数 : EndTransaction
# 概要 : ファイルトランザクションの終了
# 引数 : テンポラリファイル名
# 戻値 : なし
#------------------------------------------------------------------
sub EndTransaction
{
my $tmpfile = @_[0];
unlink $tmpfile;
}
▲-----------------------------------
排他制御の方法はいろいろとあり、サーバやシステム処理の条件によって変わって
くるのですが、上の関数はほとんどのケースで使用できる関数です。
StartTransaction関数で、まずロックファイルというものの存在を確認します。
存在する場合は、別のプロセスが書き込み処理中ということになりますので、
1秒おきにチェックし、10秒間繰り返して待ちます。
10秒たってもロックが解除されない場合は、ロックを解除し、失敗を返します。
ただし、ファイルの書き込み処理が10秒以上かかると想定される場合、待ち時間を
長くしたり、ロックを解除せずに終了するなどの処理にした方が良いと思います。
ただ、ファイルの書き込み処理が10秒以上かかるような場合は、データベースによる
保存方法にした方が良いです。また、ロックを解除せずに終了する場合、何らかの
原因でロックファイルが残ってしまった時に、手動で削除しなければならなくなり、
メンテナンスの必要が出てきますので、注意しましょう。
ロックファイルが存在しない場合は、ロックファイルを作成します。これで自分の
プロセスが、ファイル更新することができる権限を得たことになります。
ロックファイルを作成したら、その間にファイルの読み込み、更新処理を行います。
これはStartTransaction関数とEndTransaction関数の間に行います。
EndTransaction関数では、ロックファイルをクリアします。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#!/usr/local/bin/perl
use strict;
my $filename = "test.dat";
my($tmpfile, $err, $cnt);
#// トランザクション開始
$tmpfile = StartTransaction($filename);
#// 成功したかどうかのチェック
if ($tmpfile eq '')
{ }
open(FIL, "$filename") or $err = 1;
if ($err) { exit; }
$cnt = <FIL>;
close(FIL);
if ($cnt eq '') { $cnt = 0;}
#カウントをアップする
$cnt++;
#// ファイル書き込み
$err = 0;
open(FIL, ">$filename") or $err = 1;
if ($err) { exit; }
#// ファイル書き込み
print FIL $cnt;
#// ファイル開放
close(FIL);
#// トランザクション終了
EndTransaction($tmpfile);
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
あなたは $cnt人目のお客様です。
</body>
</html>
END_OF_HTML
(StartTransaction関数、EndTransaction関数をここに挿入)
exit(0);
▲------------------------------------
▼出力結果----------------------------
あなたは 11人目のお客様です。
▲------------------------------------
上記はアクセスカウンタのサンプルです。test.datという中身が空白のファイルを
同じフォルダに書き込み権限ありで作成する必要があります。
・HTMLエンコード・デコード
掲示板など、ホームページを訪れる一般ユーザに、自由に文字を記入できる
データを扱う場合、HTMLのタグを処理する必要があります。
これをしなかった場合、セキュリティホールとなりますので、注意が必要です。
例えば、一般ユーザが書き込める掲示板で、悪意のあるJavaScriptコードを記入
した場合、そのサイトが悪さするサイトになってしまいます。
これを回避するには、HTMLのタグをエスケープ(エンコード)する必要があります。
これにより、HTMLのタグ文字<>が<>というように変換され、ブラウザが、
この文字は、HTMLのタグではなく、単なる<>という文字として扱ってくれます。
デコード関数は、エンコードした文字列を、テキストボックスやテキストエリアに
戻すときに必要になります。
以下にHTMLエンコード関数と、HTMLデコード関数のサンプルを記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : HTMLEncode
# 概要 : HTMLのタグをエンコード
# 引数 : 変換前の文字列
# 戻値 : 変換後の文字列
#------------------------------------------------------------------
sub HTMLEncode
{
my $value = $_[0];
#// HTMLのエスケープシーケンスをエンコード
$value =~ s/&/&/g;
$value =~ s/"/"/g;
$value =~ s/</</g;
$value =~ s/>/>/g;
$value =~ s/\n/<br>/g;
return $value;
}
#------------------------------------------------------------------
# 関数 : HTMLDecode
# 概要 : HTMLのタグをデコード
# 引数 : 変換前の文字列
# 戻値 : 変換後の文字列
#------------------------------------------------------------------
sub HTMLDecode
{
my $value = $_[0];
my $flg = $_[1];
#// HTMLのエスケープシーケンスをデコード
$value =~ s/&/&/g;
$value =~ s/"/"/g;
$value =~ s/</</g;
$value =~ s/>/>/g;
if ($flg == 1)
{
#// テキストエリアの場合は改行を戻す
$value =~ s/<br>/\n/g;
}
return $value;
}
▲-----------------------------------
<>文字の他、&や"などもエスケープしています。
また、テキストエリアの場合、改行も<br>タグに変換する必要があります。
デコード関数の方で、テキストエリアに戻す場合は、2番目の引数に1を渡し、
<br>タグを改行に戻しています。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
my($str, $str1, $str2);
$str = "<script></script>";
$str1 = HTMLEncode($str);
$str2 = HTMLDecode($str1);
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
エンコード前 str = $str<br>
エンコード後 str1 = $str1<br>
デコード後 str2 = $str2<br>
</body>
</html>
END_OF_HTML
(HTMLEncode関数、HTMLDecode関数をここに挿入)
exit(0);
▲------------------------------------
▼出力結果----------------------------
エンコード前 str =
エンコード後 str1 = <script></script>
デコード後 str2 =
▲------------------------------------
出力結果は、ブラウザで見ると、エンコード前とデコード後の部分が表示されて
いません。
これは、<script>タグが記述されているので、ブラウザがHTMLのタグと認識して
いるためです。
エンコード後の方が表示されているのは、タグがエスケープされているためです。
ブラウザの「ソースを表示する」で、出力結果のソースを見てみると・・
▼実際のソース------------------------
<html>
<body>
エンコード前 str = <script></script><br>
エンコード後 str1 = <script></script><br>
デコード後 str2 = <script></script><br>
</body>
</html>
▲------------------------------------
となっています。ブラウザがタグを解釈する場合と、単なる文字として扱う場合が
異なることが分かると思います。
ただ、クライアントの要望で、文字の色や大きさを変えられるように<font>などの
タグを使用できるようにしてほしいという場合があります。
この場合は、一部のタグのみを許可する処理が必要になります。
・クッキーの保存、取得
クッキーとはご存知の通り、アクセスしてきたユーザのローカルPCにテキストデータ
を保存できる機能のことを言います。
Perlでは、受け渡しが少々面倒なので、また関数にしてみました。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : SetCookie
# 説明 : クッキー設定
# 引数 : 保存する文字列
# : クッキーの有効時間(秒)
# 戻値 : なし
#------------------------------------------------------------------
sub SetCookie
{
my($data, %data, $str_cookie, $cookie_time);
my($sec, $min, $hour, $mday, $mon, $year, $wday);
my(@mons, @week, $dt);
$str_cookie = @_[0];
$cookie_time = @_[1];
#// 文字列のエスケープ
$str_cookie =~ s/\,/%2C/g;
$str_cookie =~ s/ /%20/g;
$str_cookie =~ s/;//g;
#// 有効期限設定
($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime(time + $cookie_time);
@mons = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
@week = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
$dt = sprintf("%s\, %02d-%s-%04d %02d:%02d:%02d GMT", $week[$wday], $mday, $mons[$mon], $year+1900, $hour, $min, $sec);
#// ヘッダ出力
print "Set-Cookie: $str_cookie expires=$dt;\n";
}
#------------------------------------------------------------------
# 関数 : GetCookie
# 概要 : クッキー取得
# 引数 : なし
# 戻値 : クッキーの値
#------------------------------------------------------------------
sub GetCookie
{
my($cookie) = $ENV{'HTTP_COOKIE'};
my($key, $val, %data);
my(@cookie);
#// クッキー取得
@cookie = split(/ /, $cookie);
foreach(@cookie)
{
($key, $val) = split(/=/);
#// 文字列をデコードする
$val =~ s/%2C/\,/g;
$val =~ s/%20/ /g;
$data{$key} = $val;
}
#// 戻り値セット
return %data;
}
▲-----------------------------------
クッキーを保存するには、ユーザへのレスポンスのヘッダ部分に保存するデータを
記述します。
SetCookie関数は、クッキーにデータを書き込むための関数です。
この関数には、第一引数に保存するデータ、第二引数にクッキーの有効期限を渡します。
この関数は、HTTPヘッダのSet-Cookieにデータを書き込みますので、注意してください。
GetCookieの方は、クッキーを取得するための関数です。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#!/usr/local/bin/perl
use strict;
my ($cookie_name, %cook_data, $str_cookie, $count);
$cookie_name = "cname";
%cook_data = GetCookie();
$count = $cook_data{$cookie_name};
if ($count eq '')
{ }
else
{}
$str_cookie = $cookie_name . "=" . $count . ";";
SetCookie($str_cookie, 31536000);
print "Content-type: text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
END_OF_HTML
print $count;
print << "END_OF_HTML";
回目の訪問ありがとうございます。
</body>
</html>
END_OF_HTML
(GetCookieString関数、GetCookie関数をここに挿入)
exit(0);
▲------------------------------------
▼出力結果----------------------------
1回目の訪問ありがとうございます。
▲------------------------------------
ユーザがこのページを訪れた回数を表示するサンプルです。
最初にアクセスした時はクッキーに何も保存されていないので、デフォルトの値を
表示しますが、2回目からは、クッキーからデータを取得します。
クッキーを利用してユーザのローカルマシンに保存するので、アクセスカウンタと
違い、ユーザ毎に違うカウントが表示されます。
サンプルでは、クッキーの有効期限を1年に設定しています。
クッキーを削除したい場合は、有効期限を0にして保存させればOKです。
ちなみに・・・
クッキーには当然、容量制限があります。
「名前=値;名前=値;・・・」とクッキーに保存できますが、この「名前」の数が
20個まで、また、クッキーに保存するテキストデータの合計が4kバイトまでと
なっています。
・ページ遷移
Perlの実行中に他のページへジャンプさせる時は、
print "Location: ./main.cgi\n\n";
のように書きます。これは、GETによるページジャンプになります。
パラメータを渡したい場合は、
print "Location: ./main.cgi?id=1&page=2&order=new\n\n";
のように記述することもできます。
しかし、会員ログイン後にクッキーに書き込んでページ遷移したい場合などは
どうすれば良いでしょうか?
クッキーに書き込むには、一度クライアント側へクッキー情報を渡さなければ
なりません。
これを実現するには、上記の関数ではなく、一度HTMLを返し、HTMLのメタタグに
よるジャンプを利用すると良いでしょう。
以下にその関数例を記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : PageJump
# 概要 : リフレッシュによる画面遷移
# 引数 : $page 遷移先
# : $strCookie クッキー文字列
# 戻値 : なし
#------------------------------------------------------------------
sub PageJump
{
my $page = @_[0];
my $strCookie = @_[1];
print "Content-type: text/html\n";
if ($strCookie ne '')
{
}
print << "END_OF_HTML";
<html>
<head>
<meta http-equiv="refresh" content="0;url=$page">
</head>
</html>
END_OF_HTML
}
▲-----------------------------------
こうすることにより、一度クッキー付きの空白のページを渡し、0秒で次のページに
ジャンプします。
この関数の使用例を一応、以下に記述します。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
PageJump("./main.cgi?id=1&page=2&order=new", "id=0001,pass=Waj4c9");
(PageJump関数をここに挿入)
exit;
▲-----------------------------------
サーバ⇔クライアントのやり取りが一つ増えるので、若干時間がかかってしまい
ますが、回線速度がよっぽと遅くない限りは問題ない範囲です。
次に、同じくページジャンプする場合ですが、GETではなく、POSTでジャンプしたい
場合はどうすれば良いでしょうか。
PerlではPOSTによるジャンプはできませんので、一度クライアントにHTMLを返して
そのHTMLからJavaScriptによってPOSTを送らさせるしかないようです。
以下に、これを関数化したサンプルを記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : PageJumpPost
# 概要 : Postによる画面遷移
# 引数 : $page パラメータ付きURL
# : $strCookie クッキー文字列
# 戻値 : なし
#------------------------------------------------------------------
sub PageJumpPost
{
my $url = @_[0];
my $strCookie = @_[1];
my $page = substr($url, 0, index($url, "?"));
my $param = substr($url, index($url, "?") + 1);
my ($name, $value);
#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
my @a = split(/&/, $param);
print "Content-type: text/html\n";
if ($strCookie ne '')
{
}
print << "END_OF_HTML";
<html>
<head>
<script language="JavaScript"><!--
function jump()
{
}
//--></script>
</head>
<body onLoad="jump();">
<form name="f1" action="$page" method="post">
END_OF_HTML
foreach $a (@a)
{
#// =(イコール)で分解
($name, $value) = split(/=/, $a);
print "<input type=\"hidden\" name=\"$name\" value=\"$value\">\n";
}
print << "END_OF_HTML";
</form>
</body>
</html>
END_OF_HTML
}
▲-----------------------------------
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
PageJumpPost("./main.cgi?id=1&page=2&order=new", "id=0001,pass=Waj4c9");
(PageJumpPost関数をここに挿入)
exit;
▲-----------------------------------
先ほどの関数と同じく、クッキー情報を渡し、さらにフォームのHIDDENタグに
パラメータを出力し、ページロード時にすぐにPOSTによるサブミットを行って
います。
ただし、この方法はあまり良い方法とは言えません。どうしてもという場合は
仕方ないですが、なるべく設計段階で、このようなCGIからCGIへPOSTジャンプする
ことがないよう設計しましょう。
・ファイルの読み込み・書き込み時のチェック
ファイルの読み込み時に行った方が良い処理と、ファイル書き込み時に行っ
た方がよい処理をご紹介します。
ファイルの読み込み時は、以下のようにしますね。
▼サンプル----------------------------
if (!open(FILE, "$file_name"))
{
}
▲------------------------------------
ただ、ファイルは読み込まないが、ファイルが存在しているかをチェックしたい場合
があるかと思います。
その場合は、以下のようにします。
▼サンプル----------------------------
if (-f $file_name)
{
$message = "$file_nameはあります。\n";
}
else
{
$message = "$file_nameはありません。\n";
}
▲------------------------------------
また、ファイル書き込み時の注意点として、保存しようとしているファイル容量で
書き込み可能のチェックをした方が安全と言えます。
例えば、共有のレンタルサーバなどで使用可能なハードディスク容量が決まっている
場合、WEBサーバのログファイルなどがたまって、使用できるハードディスク容量が
いっぱいになった後にファイル保存しようとすると、保存に失敗してファイルが
消えてしまう可能性があります。
普通のレンタルサーバならログの自動削除があるので問題ないと思いますが、不親切
なサーバだとほったらかしなので、念のためファイル書き込みの確認処理をすること
をお薦めします。
以下はその関数例です。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : CheckFileWrite
# 概要 : ファイルが書き込み可能かを判定する
# 引数 : ファイル名、書き込むデータ
# 戻値 : 1:書き込み可、0:書き込み不可
#------------------------------------------------------------------
sub CheckFileWrite
{
my $size;
my $filename = @_[0];
my $data = @_[1];
my $tmpfile = $filename;
$tmpfile =~ s/(\w+)\.\w+$/$1\.tmp/i;
if (!(open(TMP, ">$tmpfile")))
{
}
print TMP $data;
close(TMP);
$size = -s $tmpfile;
if ($size == 0 && length($data) > 0)
{
}
unlink $tmpfile;
return 1;
}
▼サンプル----------------------------
上記では、渡した文字列で可能かどうかを確認し、別名のファイルで保存しており、
失敗した場合は0を返し、成功した場合は、一時ファイルを削除して1を返します。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
my $file_name = "test.dat";
my $file_data = "ファイルに書き込むデータです。";
my $message;
if (CheckFileWrite($file_name, $file_data))
{
}
else
{
}
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
$message
</body>
</html>
END_OF_HTML
(CheckFileWrite関数をここに挿入)
exit;
▲------------------------------------
▼出力結果----------------------------
保存可能です。
▲------------------------------------
もちろん、ファイル本体のオープンチェックは必ず行う必要があります。
▼サンプル----------------------------
if (!open(FILE, ">$file_name"))
{
}
▲------------------------------------
といった感じですね。
・パスワードの自動生成
よく会員登録時に、パスワードが自動的に生成されるシステムがありますが、Perl
でパスワードを自動生成する方法をご紹介します。
以下にその関数例を記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : AutoPassword
# 概要 : パスワード自動生成
# 引数 : なし
# 戻値 : 生成されたパスワード
#------------------------------------------------------------------
sub AutoPassword
{
my (@salt, $password);
push @salt, 'A'..'H', 'J'..'N','P'..'Z';
push @salt, 'a'..'k', 'm'..'z';
push @salt, '2'..'9';
for (1 .. 8)
{
$password .= "$salt[int(rand($#salt+1))]";
}
return $password;
}
▲------------------------------------
簡単ですね。パスワードとして使用する文字を配列に格納し、ランダム関数(rand)を
使用して8文字のパスワードを生成しています。
大文字の「I」、「O」や小文字の「l」、数字の「1」、「0」を除外しているのは、
見た目が似ていて、入力する時に間違いやすいからです。
for (1 .. 8)の部分の8の数字を変えれば、パスワードの文字数を変えられます。
この関数の使用例は、以下のようになります。
▼CGIサンプル-------------------------
#! /usr/local/bin/perl
use strict;
my $pass = AutoPassword();
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=Shift_JIS">
<TITLE>TEST</TITLE>
</HEAD>
<BODY>
$pass
</BODY>
</HTML>
END_OF_HTML
(AutoPassword関数をここに挿入)
exit;
▲------------------------------------
更新ボタンを何度も押すと、毎回違う結果が出力されるのが分かるかと思います。
・電話番号やメールアドレスのチェック方法
お問い合わせフォームや会員登録フォームなどで、よく電話番号やメールアドレスの
入力があると思いますが、ここでは電話番号やメールアドレスのチェック方法を
ご紹介します。
電話番号チェックでは、半角数字とハイフンのみ許可し、メールアドレスチェックで
は、半角英数字のチェックと、***@***.***と、アットマークとドットのチェックも
行います。
以下にその関数例を記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : CheckNumber
# 概要 : 半角数字、ハイフンのチェック
# 引数 : チェック文字列
# 戻値 : 1:正しい、0:正しくない
#------------------------------------------------------------------
sub CheckNumber
{
}
#------------------------------------------------------------------
# 関数 : CheckMail
# 概要 : メールアドレスチェック
# 引数 : メールアドレス
# 戻値 : 1:正しい、0:正しくない
#------------------------------------------------------------------
sub CheckMail
{
}
▲-----------------------------------
電話番号のチェックは郵便番号のチェックにも使えます。
また、フォームのデザインによっては[ ]-[ ]-[ ]のように、最初からハイフンが
表示されて、入力ボックスが3つに分かれている場合がありますが、その場合は
上記の関数は使用できません。
その場合は、CheckNumber関数の
if ($str !~ /^[0-9]([0-9]|-)+$/)
の部分を
if ($str !~ /^[0-9]([0-9])+$/)
として、各ボックスの半角数字のみをチェックすればOKです。
この関数の使用例は、以下のようになります。
▼CGIサンプル(/cgi-bin/form.cgi)-------------------------
#!/usr/local/bin/perl
print "Content-type: text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
<form method="post" action="./form_check.cgi">
電話番号 <input type="text" name="tel" size="30"><br>
メールアドレス <input type="text" name="mail" size="30"><br>
<input type="submit" name="submit1" value="送信">
</form>
</body>
</html>
END_OF_HTML
exit(0);
▲-----------------------------------
▼CGIサンプル(/cgi-bin/form_check)-------------------------
#!/usr/local/bin/perl
use strict;
my $message;
$message = "";
my %param;
%param = &GetPara();
#電話番号のチェック
if ($param{'tel'} eq '')
{
$message .= "電話番号が入力されていません。<br>\n";
}
elsif (CheckNumber($param{'tel'}) == 0)
{
$message .= "電話番号に誤りがあります。<br>\n";
}
#メールアドレスのチェック
if ($param{'mail'} eq '')
{
$message .= "メールアドレスが入力されていません。<br>\n";
}
elsif (CheckMail($param{'mail'}) == 0)
{
$message .= "メールアドレスに誤りがあります。<br>\n";
}
if ($message eq '')
{
$message .= "入力内容に誤りはありませんでした。<br>\n";
}
print "Content-type: text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
$message
</body>
</html>
END_OF_HTML
#------------------------------------------------------------------
# 関数 : GetPara
# 概要 : パラメータ取得
# 引数 : なし
# 戻値 : パラメータハッシュ配列
#------------------------------------------------------------------
sub GetPara
{
my $self = shift;
my($query_string); #// エンコードされたパラメータ全体
my(@a, $a); #// エンコードされたパラメータを分解したもの
my($name, $value); #// デコードされたパラメータ
my(%in);
#// パラメータの読み込み
if ($ENV{"REQUEST_METHOD"} eq "POST")
{
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{"CONTENT_LENGTH"});
}
else
{
#// GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}
#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);
# パラメータの取得
foreach $a(@a)
{
#// =(イコール)で分解
($name, $value) = split(/=/, $a);
#// + や %8A などのデコード
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex( $1 ))/eg;
#// 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{$name} = $value;
}
return %in;
}
(CheckNumber関数とCheckMail関数をここに挿入)
exit;
▲-----------------------------------
パラメータを取得するため、以前ご紹介したGetParamを使用しています。
上記の例は、電話番号とメールアドレスが必須の場合を想定していますが、
必須でない場合は、空文字チェックのif文をはずせばOKです。
・URLエンコード・デコード
CGIプログラムの処理中に、別のページにジャンプさせた時に、パラメータに日本語
を渡すと文字化けしてしまうという問題が生じます。
ページジャンプ時に日本語を渡す場合は、URLエンコードを行い、日本語を半角
英数字に変換してあげる必要があります。
受け取った側は当然デコードが必要になります。
よくブラウザのアドレスバーに
http://www.google.co.jp/search?hl=ja&q=%E6%A4%9C%E7%B4%A2%E3%83%86%E3%82%B9%E3%83%88&lr=
(Google検索結果のURLの例)
といった表記を見たことがあるかと思いますが、このパラメータの%・・というのが
日本語のエンコードされたものになります。
以下にURLエンコードとデコードの関数例を記述します。
▼サンプル----------------------------
#------------------------------------------------------------------
# 関数 : URLEncode
# 概要 : 文字列をURLエンコードする
# 引数 : 変換前の文字列
# 戻値 : 変換後の文字列
#------------------------------------------------------------------
sub URLEncode
{
my $str = $_[0];
$str =~ s/(\W)/'%'.unpack("H2", $1)/ego;
$str =~ tr/ /+/;
return $str;
}
#------------------------------------------------------------------
# 関数 : URLDecode
# 概要 : 文字列をURLデコードする
# 引数 : 変換前の文字列
# 戻値 : 変換後の文字列
#------------------------------------------------------------------
sub URLDecode
{
my $str = $_[0];
$str =~ tr/+/ /;
$str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/ego;
return $str;
}
▲-----------------------------------
引数で渡された文字列を変換処理しているだけです。
この関数の使用例は、以下のようになります。
▼CGIサンプル(/cgi-bin/encode.cgi)-------------------------
#! /usr/local/bin/perl
use strict;
my($str, $str1);
$str = "テストtest";
$str1 = URLEncode($str);
print "Location: ./decode.cgi?data=$str1\n\n";
(URLEncode関数をここに挿入)
▲-----------------------------------
▼CGIサンプル(/cgi-bin/decode.cgi)-------------------------
#! /usr/local/bin/perl
use strict;
my (%data, $str);
#// パラメタ取得
%data = &GetPara();
$str = $data{'data'};
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
str = $str<br>
</body>
</html>
END_OF_HTML
#------------------------------------------------------------------
# 関数 : GetPara
# 概要 : パラメータ取得
# 引数 : なし
# 戻値 : パラメータハッシュ配列
#------------------------------------------------------------------
sub GetPara
{
my $self = shift;
my($query_string); #// エンコードされたパラメータ全体
my(@a, $a); #// エンコードされたパラメータを分解したもの
my($name, $value); #// デコードされたパラメータ
my(%in);
#// パラメータの読み込み
if ($ENV{"REQUEST_METHOD"} eq "POST")
{
#// POSTなら標準入力から読み込む
read(STDIN, $query_string, $ENV{"CONTENT_LENGTH"});
}
else
{
#// GETなら環境変数から読み込む
$query_string = $ENV{"QUERY_STRING"};
}
#// 「変数名1=値1&変数名2=値2」の形式をアンパサンド(&)で分解
@a = split(/&/, $query_string);
# パラメータの取得
foreach $a(@a)
{
#// =(イコール)で分解
($name, $value) = split(/=/, $a);
#// + や %8A などのデコード
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex( $1 ))/eg;
#// 後で使用するため,$in{'パラメータ名'} に代入しておく
$in{$name} = $value;
}
return %in;
}
(URLDecode関数をここに挿入)
exit;
▲-----------------------------------
パラメータを取得するため、以前ご紹介したGetParamを使用しています。
encode.cgiを実行すると、decode.cgiにジャンプし、ブラウザのアドレスバーに
http://localhost/cgi-bin/decode.cgi?data=%8c%9f%8d%f5%83e%83X%83gTEST
のように表示されているかと思います。
CGIファイルを保存した日本語コードがShift-JISやEUCによって違うと思いますが、
渡されたCGIの方でちゃんと日本語が表示されているかと思います。
もともと半角英数字だった「TEST」の部分は変換する必要がないので、パラメータに
そのまま表示されます。
また、変換前の文字列を渡してみると、decode.cgiの出力結果が文字化けして
出力されてしまうのが分かると思います。
|