|
データの記録
ソース
ファイルにデータを記録しておく例です。
入力チェックがありませんが、この点は後でまたこのプログラムを例にしてお話します
ので、ご了承ください。
入力チェックの要らないゲームのハイスコア記録などはこのまま応用して作成できると
思います。
用意するもの
・data.dat データ保存用ファイル
・lockfile ロックファイル
・filetest02.html HTMLの基
・filetestlib.pm ライブラリ
・filetest02.cgi プログラム
今回からは分かりやすいように拡張子を変えていますが、実態は今までと同じテキスト
ファイルです。
|
大まかな流れは、
「入力画面表示→文字入力→てきとーに数値変換→数値順に並べ替えて保存→入力画面表示
」
「入力画面」には「入力フォーム」と「入力結果」と「過去のベスト10」を表示しま
す。入力前やリロード時の「過去のベスト10」は data.dat から取り込みます。入力後
は処理結果から表示します。
というような感じで、以下ファイルの詳細です。
・data.dat
・lockfile
上記2つは空ファイルです。
メモ帳で新規作成保存して名前を変更してください。
data.dat には後から保存用のデータが書き込まれます。
(今回はカンマ区切りのテキストデータを記録します)
lockfile はデータ更新時に一時的にプログラムが名前を変更して処理終了後にまた名
前を元に戻します。このことにより、他の人がデーターファイルを更新しようときに、こ
のファイル名を見て処理中かフリーなのかを判断するようにします。
・filetest02.html
<HTML>
<HEAD><TITLE><TMPL_VAR title></TITLE></HEAD>
<BODY>
<TMPL_VAR title><BR>
<form action="filetest02.cgi" method="post">
ひとこと入力して下さい。<br>
<input type="text" name="word" size="16" maxlength="20">
<input type="hidden" name="rm" value="update">
<input type="submit" value="登 録">
</form>
<TMPL_VAR input><BR>
・・・・・・・・・・・・・・・・・・・・・・・<BR>
<TMPL_VAR testvalue><BR>
・・・・・・・・・・・・・・・・・・・・・・・<BR>
</BODY>
</HTML>
|
・filetestlib.pm
package filetestlib;
sub lock_start {
my $lockfilename;
#'lockfile'があればロック操作して終わり
#無ければ1秒待ってやり直し
for (my $i = 0; $i < 6; $i++, sleep 1) {
if (rename('lockfile', $lockfilename = 'lockfile'.time)){;
return $lockfilename
}
}
#↑6秒待っても駄目なら、ファイルを検証してみる
#まずはディレクトリ内のファイル名一覧を取得
opendir(MYDIR, '.');
my @filelist = readdir(MYDIR);
closedir(MYDIR);
#その中からルール通りのファイルをピックアップ
foreach (@filelist) {
#lockfile+数字又はlockfileというファイルは有るか?
if (/^lockfile(\d+)|^lockfile/) {
#有れば数字の部分だけ検証
#タイムアウトしたしたファイルだったらロック操作して抜ける
if (time - $1 > 6 and
rename($_, $lockfilename = 'lockfile' . time)){
return $lockfilename
};
}
}
#ここまで来たということはロックファイルが無いということ
open(LOCK,"> $lockfilename");
close(LOCK);
return $lockfilename;
}
sub lock_end {
rename($_[0], 'lockfile');
}
#HTML出力ルーチン
sub html {
#値をセット
my ($template, $data) = @_;
#HTMLテンプレートを読み込み
open(IN,$template);
my @html = <IN>;
close(IN);
my $html = join "", @html;
#HTML内の所定の文字を値と入替え
for(keys %$data){
$html =~ s/<TMPL_VAR $_>/$data->{$_}/g;
}
#出力
print "content-type: text/html; charset=Shift_JIS\n\n";
print $html;
}
return 1;
|
・filetest02.cgi
#!c:/perl/bin/perl
use CGI;
require filetestlib;
my $form = new CGI;
my $rm = $form->param('rm');
#処理分岐
$rm = 'list' unless $rm;
&$rm;
#一覧表示
sub list{
my $input = shift; #引数が有ればセット
#データファイル読込
open(IN,"data.dat");
my @data = <IN>;
close(IN);
#データファイル加工
map{s/(.+),(\d+)/"「$1」のパワーは " . sprintf("%d", $2) . "ポイントです。"/e}@data;
my $data = join "<BR>", @data;
#HTMLファイルにセットする値を作成
my %data=(
title =>"萌えファイルテスト2",
testvalue=>$data,
input =>$input
);
#HTML出力ルーチンで出力
filetestlib::html("filetest02.html",\%data);
}
#更新処理
sub update{
#入力文字取得
my $word = $form->param('word');
#入力無しなら表示のみ
unless($word){&list; exit()}
#ロックする
my $lockfile = filetestlib::lock_start() or die '処理失敗(>_<)ノ';
#文字→暗号化→数値以外を除外で数値化
my $length = length($word);
my $power = crypt($word,$length);
$power=~s/\D//g;
$power=sprintf("%012d", $power); #前に0をつけて桁揃え
my $newrec = "$word,$power\n"; #ニューレコード
#データファイルに無ければ書き足し
open(IN,"<data.dat");
@data=<IN>;
close(IN);
my $data = join ":", @data; #検索用に1列化
if($data !~ m/$newrec/oms){
push (@data, $newrec);
}
#2番目の項目で並べ替え(大きいもの順)
my @tmp = map {(split /,/)[1]} @data;
@data = @data[reverse sort{$tmp[$a] cmp $tmp[$b]} 0 .. $#tmp];
my $max=@data;
if($max>10){pop(@data)} #データが10個超えてたらお尻を削除
#データセーブ
open(OUT,">data.dat");
print OUT @data;
close(OUT);
#ロック解除
filetestlib::lock_end($lockfile);
#一覧表示
my $input = "「$word」のパワーは " . sprintf("%d", $power) . "ポイントです。";
&list($input);
}
|
これらのファイルを同じところに置いて filetest02.cgi をブラウザで読み込むと以下
のように表示されます。
ここで文字を入力して登録ボタンをクリックします。
例として「萌え」「Perl」と入力してみると以下のようになります。
こんな調子で最高10行まで保存されます。
★☆実際の動作確認はこちら。☆★
*・゜゚・*:.。..。.:*・゜(n‘∀‘)η゚・*:.。..。.:*・゜゚・* !!!!!
サブルーチンを分けました
このプログラムは「ブラウザーから入力データーを受け取る」「記録する」そして「サ
ブルーチンを外部ファイルに書いて置く」のサンプルです。
詳しくは別コーナーで説明しますが、プログラムは1枚のファイルに全てを書き込む必
要は無くて、複数のファイルに分けて書くことが出来ます。
特に使いまわしができる部分は独立したファイルに分けて置くと他のプログラムとも共
有できるので便利です。
ひとまず簡単に説明すると
・分けられたファイル
・1行目に package ファイル名 と書く。
・最終行に 1 と書く。
・拡張子を .pm にする。
・本体プログラム
・use ファイル名
又は require ファイル名 と書く。
|
図にするとこんな感じ。
・分けられたファイル(hoge.pm)
package hoge
sub hage {
処理
}
sub piyo {
処理
}
1
|
・本体プログラム
#!c:/perl/bin/perl
require hoge;
hoge::hage();
hoge::piyo();
|
これで、本体のプログラムファイルから分けられたファイルの内容を取り込むことが出
来ます。詳しいことは後のモジュールのコーナーで説明しますので、ここではこれまで。
ファイルロックとHTML生成のサブルーチンは汎用性が高いので、他のプログラムか
らも取り込めるように分けました。
汎用性の無いものやこの先2度と再利用しないようなものなら無理に分ける必要はない
でしょう。
ただ、部品ごとに分けておくとプログラム自体がスッキリとして見やすくはなるので、
お好みに応じて判断してください。
ファイルの処理
基本的にファイルを読み込むだけならロックしない。更新するならロックする。という
感じでプログラムを組みました。
data.dat に保存する形式は以下のようになります。
入力文字,数値
入力文字,数値
入力文字,数値
これを読み込んで表示する時には、
map{s/(.+),(\d+)/"「$1」のパワーは " . sprintf("%d", $2) . "ポイントです。"/e}@data;
で、
「Perl」のパワーは 48ポイントです。
「萌え」のパワーは 4ポイントです。
のように文字を加えます。
保存する時には数値の大きいもの順に並べ替えます。
my @tmp = map {(split /,/)[1]} @data;
@data = @data[reverse sort{$tmp[$a] cmp $tmp[$b]} 0 .. $#tmp];
これで、2番目の項目で大きいもの順になります。
あと、入力した文字を数値に変換する部分ですが、
my $length = length($word);
my $power = crypt($word,$length);
$power=~s/\D//g;
crypt が暗号化する命令で、使い方は
変数 = crypt("変換される文字","キー"); です。
このとき、変換される文字は頭から8バイト、キーは頭から2バイトしか参照されませ
ん。なので、変換される文字が半角8文字、全角なら4文字以上は何を入れても同じ結果
になってしまうので、キーのところに文字数を入れるようにしました。
(これでもあたま4文字が同じで文字数が同じものなら同じ結果が出るので完璧にユニー
クにはならないのですが、まあ本題とは関係ないしテストですので)
なんてふうにテキトーに数値変換してみました。
保存する時には数値のあたまを0で埋めています。これはさっきの並べ替えのキーとし
てこの項目を文字扱いしているからです。
ただ、もともとが数値なので以下のようにすればべつにいいです。
my @tmp = map {(split /,/)[1]} @data;
@data = @data[reverse sort{$tmp[$a] cmp $tmp[$b]} 0 .. $#tmp];
↓
my @tmp = map {(split /,/)[1]} @data;
@data = @data[reverse sort{$tmp[$a] <=> $tmp[$b]} 0 .. $#tmp];
とか、数値変換と並べ替えは蛇足でしたが、今回はこんな感じで。
|
|
|