Perl/CGI研究室 'PERL-LABO'

Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

ログ管理の研究

研究内容

ページビューカウンターのログを一定間隔でリセットします。 過去のログをいくつか保存してそれも閲覧できるようにします。

詳細

今までのカウント方式

今まではCGIプログラムを設置してからの全てのアクセス記録を1つのファイルに保存していました。 これだと、昔からあるページはカウントが多く、新しいページはカウントが少ないというように なってしまいますので、あんまり良くありません。 なんとか、うまく改良したいです。

改良案

まず、一定間隔で自動的にデータをクリアして最初からカウントするようにするっていうのが 考えられます。こうすれば、常に最新のデータが見られますよね。 でもこれだと、リセットした直後ってデータが集まっていなくて寂しいし データは沢山ないと信憑性にかけるっていうのもありますし、 単にリセットするだけだと折角蓄えたデータを捨てるわけですからもったいない。 ということで、リセットするときにそのデータを別のファイルにそっくり保存しておいて、 そのデータも後で閲覧できるようにしたらいいですね。

基本方針は「一定期間毎でデータをリセットする」「過去のデータも閲覧できるようにする」です。

結果

具体的な修正方法を考える

修正方針は決まっていますが、これ結構難しいですよ。 1つ1つ考えていきます。

一定期間経ったことを知る方法

アクセスカウンターのときは1日に1回カウントをリセットするっていうことをしました。 これは、日付が変わったのを検出したときに処理をするんでした。 単に日付が変わったタイミングでリセットするっていうのなら、難しくありませんでした。 でも、ページビューカウンターはカウントそのものよりも、どのページが見られているのかっていう ことを知りたいっていうことの方が大きいですし、このサイトのようにアクセスが少ないサイトの場合は 1日だけだと閲覧数が少なくてどのページがよく見られているのか良く分かりません。 ですので、リセットするタイミングは1日1回ではなくて、例えば5日に1回とか10日に1回とかに できた方が嬉しいわけです。ですからそのようにしたいと思います。

それじゃ、それはどうやってやるのかっていうことですよね。 処理をするタイミングとしては、やっぱりキリのいいところで午前0時ということにしましょう。 午前0時になったときに、 正確には日付が変わったあとの最初のアクセスのときですが、 前回リセットしたときから指定した日数経っていたら、 データをリセットするという感じになります。 さて、今まで、日付や時刻を扱うときには、localtime(time()) を使いました。 これは、現在の日付や時刻を得ることが出来るんでした。 前回リセットした日付と時刻というのは、これを使って保存しておくことができます。 ここで問題は、前回の日付、時刻が分かっているときに、そこからの経過日数をどうやって知ったらいいのか、 ということです。

ここで、time 関数について復習してみましょう。

time 関数 - 1970年1月1日0時0分0秒からの経過秒数を返す。

なるほど。time 関数の戻り値そのものは秒で、 localtime 関数でこれを年月日時分秒に変えることができるんでした。 ということは、localtime 関数を使わずに、time 関数の戻り値を使えば…。 つまり、現在の time から、前回リセットしたときの time を引くと、それは 前回から現在までの経過秒数になります!これは使えそう。

ちょっと方向が見えてきました。 経過秒数が分かれば、1日は(60x60x24)秒ですから経過日数も分かります。

しかしこれ、実際に試してみて、いくつか問題があることが分かりました。 前回のリセット時刻が 午前0時3分 だったとしますよね。 つまり、午前0時3分 がその日の最初のアクセスだったと。 そして、次の日のアクセスが 午前0時2分 だった場合、日付は変わっていますが、 24時間経っていません。これ、微妙に難しい問題ですね…。 この問題を解決するには、1970年1月1日からの経過秒数じゃなくて、 経過日数が必要です。

で、time 関数の戻り値を(60x60x24)で割ったものは1970年1月1日からの経過日数を表すわけですから、 最初からこの経過日数の形で処理を行うようにすれば、 現在の経過日数 - 前回リセットしたときの経過日数 = 前回リセットしてからの経過日数 となり、こちらの方が分かりやすそうだし、上に書いた問題も解決するのでは、と考えました。 …しかし、まだ問題がありました。 というのは、1970年1月1日0時0分0秒 っていつ?という問題です。 パソコンは世界中にあり、場所によって時差があります。 そのため、time 関数の戻り値から日付時刻を得るのにも、gmtime 関数と localtime 関数の2つがあるんでした。 それじゃ time 関数の 1970年1月1日0時0分0秒 っていつ?これ、試してみると、どうも gmtime に対応する方の日付、時刻みたいなんです。グリニッジ標準時というやつです。 単純に time 関数の戻り値を(60x60x24)で割ったものは、日本での1970年1月1日からの経過日数になりません。 で、うまくいきません。

それじゃ別の方法を、と調べていていいものを見つけました。それは localtime 関数の戻り値です。 年月日時分秒 の他に、1月1日からの経過日数も教えてくれるんです。 これを使えば?と思ったんですが、これも問題あり。 1月1日からの経過日数ということで、年をまたぐと 0 に戻ってしまうんです。 これはややこしい。もしかして、閏年のこととか考えなきゃいけなくなりそう (^^; で、これもボツ。

そして、 結局、自力で time 関数の戻り値を日本での1970年1月1日からの経過秒数に補正しようという感じになりました。 グリニッジ標準時と日本の時刻は9時間ずれています。日本の方が9時間進んでいます。 ですので、time() に 60x60x9(9時間分の秒数)を足したものが日本での1970年1月1日からの経過秒数になります。 で、これを 60x60x24 で割れば、日本での、1970年1月1日からの経過日数になります。 ちょっと無理矢理っぽいですね。このやり方は日本でしか通用しません。 もし私が作ったCGIプログラムを外国の人が使ったら、微妙におかしなことになります。 でも、これを使えば1970年1月1日からの経過日数が分かり、 それを使って、前回リセットからの経過日数が分かると。 これでいきましょう!もっといい方法があるのかも知れませんが…。 これが現在の精一杯です。

これで、一定日数が経ったことを知る方法は分かりました。 ちなみに、ここでは経過日数が問題でしたが、経過時間、経過分、なども一応同じやり方でできます。

データの保存方法

ページビューカウンターのデータは連想配列に入れて、その連想配列をファイルに保存しているんでした。 それでは、前回リセットしたときの経過日数とか、そういうデータをどこに保存しておきましょうか。 1つの方法は、別のファイルに保存すること。 しかし、前もどこかで書きましたがファイルアクセスというのはサーバーに負荷がかかります。 できるだけ、少ないファイルで情報を処理するようにしたいです。 無理してそうする必要もありませんが、基本方針としては、ファイルの数は少なく!です。

そこで、連想配列の中にこういった内部的に必要なデータを保存しておく方法を考えました。 連想配列を %hash として、

$hash{'_キー'} = 値;

連想配列のキーはこのCGIプログラムではURLを使うことになっていましたが、 URL以外のものも入れちゃうんです。そのかわり、キーの頭に _ (アンダーバー)を付けて、 _ が付いているものはなにかURLでないデータですよ、というようなルールにするんです。 URLは http://〜 で始まりますから区別できます。こうすれば、連想配列に必要なデータを 好きなだけ含ませることができますよね。 問題は、ページビューカウントをグラフ表示するときとかに、このデータを連想配列から削除するっていう 処理が必要になる点ですが、それほど大変な処理でも無いと思うし、このやり方でやってみようと思います。

過去のデータ保存しておく方法

データをリセットするときに、現在のデータを別ファイルにして後で閲覧できるようにしたいんでした。 これはどうやりましょうか。ファイル名を変更しちゃえばいいですよね。 例えば、data.cgi というデータファイルがあったら、data.1.cgi というように名前を変えちゃうんです。 で、新たに data.cgi にデータを保存すると。

ちなみに、data.cgi.1 というように末尾に数字を付けるのは良くありません。 他人に丸見えになっちゃうかも知れませんから。 データファイルの拡張子は cgi にするっていうのを忘れないようにしましょう。 見られても構わないファイルの拡張子は cgi でなくてもいいですが、 間違えてパスワードが書かれているファイルを丸見えにしちゃったりしないように 注意しないといけませんよね。

これで、過去1回分のデータを保存しておく方法は分かりました。 ここでさらに、ちょっと欲張って、過去1回分だけじゃなくて、好きな数だけ保存しておく方法を考えましょう。 data.1.cgi data.2.cgi data.3.cgi… というように番号を付けて管理すれば いいはずです。何個分の過去データを保存しておくかっていうのも、指定できるようにしましょう。

その他の修正

グラフ表示関数で過去ログも見れるようにする、 データを取った開始〜終了日付も表示するようにする、 という機能の追加をしましょう。

作成したCGIプログラム

pvcounter.cgi
#!/usr/bin/perl

require 'pvcounter.pl';
require 'counters.pl';
require 'getformdata.pl';

$fname   = 'pvcounter.txt';	# データファイル名
$nlog    = 3;			# 保存する過去ログの数
$logspan = 7;			# ログを回転する日数間隔

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

%form = plab::getformdata();

if ($form{'mode'} ne "graph") {
	print plab::countpageview($fname, $nlog, $logspan);
}
else {
	$maxgraphwidth = 200;	# グラフの棒の最大長さ
	$logno = $form{'log'};	# 表示するログ番号
	plab::printgraphwithhref_counters($fname, $maxgraphwidth, $nlog, $logno);
}
pvcounter.pl
# v 1.01

package plab;

require 'counters.pl';

# 引数(ファイル名, 保存するログ数, ログ回転日間隔)
# ページビューをカウントします
sub countpageview
{
	local $fname   = $_[0];
	local $nlog    = $_[1];
	local $logspan = $_[2];

	local $count;

	$ref = $ENV{'HTTP_REFERER'};

	if ($ref ne "") {
		$count = plab::incl_counters($fname, $ref, 1, $nlog, $logspan);
	}

	return $count;
}

1;
counters.pl
# v 1.01

package plab;

require 'getformdata.pl';
require 'hashfile.pl';
require 'lock.pl';
require 'stdplab.pl';

# 引数(ファイル名, カウントを上げるキー,
#	IPアドレス重複チェックをするかどうか1/0,
#	保存するログ数, ログ回転日間隔)
# 戻値(カウント)
sub incl_counters
{
	local $fname   = $_[0];
	local $key     = $_[1];
	local $ipcheck = $_[2];
	local $nlog    = $_[3];
	local $logspan = $_[4];

	local $lockdir = $fname . ".lockdir";
	local $ip      = $ENV{'REMOTE_ADDR'};

	# 引数チェック
	if ($logspan <= 0) {
		return "ARG ERROR";
	}

	# ロック
	if (! plab::lock($lockdir)) {
		return "ERROR";
	}

	# データ読み込み
	local %counters = plab::readhashfile($fname);

	# バージョン互換:データ取得開始日付記録
	if (! exists $counters{'_STARTDATE'}) {
		$counters{'_STARTDATE'}     = plab::getcurrentdatestring();
		$counters{'_STARTDATEINT'}  = plab::getcurrentdateint();
	}

	# 経過日数チェックとログ保存
	local $todaydateint = plab::getcurrentdateint();
	if ($todaydateint - $counters{'_STARTDATEINT'} >= $logspan) {
		# ログローテート
		plab::rogrotate($fname, $nlog);
		# カウントのリセット処理
		undef(%counters);
		$counters{'_STARTDATE'}     = plab::getcurrentdatestring();
		$counters{'_STARTDATEINT'}  = plab::getcurrentdateint();
	}

	# データ取得終了日付記録
	$counters{'_ENDDATE'} = plab::getcurrentdatestring();

	# カウントアップ
	local ($count, $previp) = split(',', $counters{$key});
	if ($ipcheck == 0 || $ip ne $previp) {
		++$count;
	}
	$counters{$key} = "$count,$ip";

	# データ書き込み
	plab::writehashfile($fname, %counters);

	# アンロック
	plab::unlock($lockdir);

	return $count;
}

# ランキング&グラフを出力します
# 引数(ファイル名, グラフの最大幅, 保存されているログ数, 表示するログ番号)
sub printgraphwithhref_counters
{
	local $fname         = $_[0];
	local $maxgraphwidth = $_[1];
	local $nlog          = $_[2];
	local $logno         = $_[3];

	# ログファイル名を得る
	local $logfname = getlogfname($fname, $logno);

	# データ読み込み
	local %data = plab::readhashfile($logfname);

	# 連想配列から内部データを取得、削除
	local $startdate = $data{'_STARTDATE'};
	local $enddate   = $data{'_ENDDATE'};
	delete($data{'_STARTDATE'});
	delete($data{'_ENDDATE'});
	delete($data{'_STARTDATEINT'});

	# 連想配列の値を閲覧回数のみにする
	# 不要なデータを削除する
	local($k, $v);
	while (($k, $v) = each(%data)) {
		($count, $ip) = split(',', $v);
		$data{$k} = $count;
	}

	# 閲覧回数順にソートしたURLの配列を作成
	local @sorted_keys = sort { $data{$b} <=> $data{$a} } keys(%data);

	# 閲覧回数1回に対応するグラフの棒の長さを算出
	local $maxval = $data{$sorted_keys[0]};
	if ($maxval == 0) { $maxval = 1; }
	local $pixelperval = $maxwidth / $maxval;

	# 閲覧回数の合計を算出
	local @v = values(%data);
	local $tot = 0;
	foreach (@v) {
		$tot += $_;
	}

	# 以下、HTMLの出力

	print "<style type=\"text/css\">";
	print "<!--";
	print "span.a { background-color: #5050FF; }";
	print "-->";
	print "</style>";
	print "<center>";

	# 過去ログ閲覧用
	local %form = plab::getformdata();
	local $script = $ENV{'SCRIPT_NAME'};
	for (0 .. $nlog) {
		local $linkname;
		local $logfname = getlogfname($fname, $_);
		if (-e $logfname) {
			if ($_ == 0) { $linkname = "現在"; }
			else         { $linkname = "過去" . $_; }
			$form{'log'} = $_;
			local $query = plab::formdata2query(%form);
			print "<a href=$script?$query>$linkname</a> ";
		}
	}
	print "<br><br>";

	print "$startdate 〜 $enddate<br><br>";
	print "<table cellpadding=5 border=1 align=center>";
	print "<tr><th>URL</th><th>回数</th><th>割合</th><th>閲覧回数グラフ</th></tr>";
	for (0 .. @sorted_keys - 1) {
		$url = $sorted_keys[$_];			# URL
		$val = $data{$url};			# 閲覧回数
		$width = $val * $pixelperval;		# グラフ長さ
		$per = sprintf("%.1f", $val / $tot * 100);	# 割合
		print "<tr>";
		print "<td nowrap><a href=$url target=_blank>$url</a></td>";
		print "<td align=right>$val</td>";
		print "<td align=right>$per%</td>";
		print "<td><span class=a style=\"padding-left:$width\"> </span></td>";
		print "</tr>";
	}
	print "</table>";
}

1;
stdplab.pl
# v 1.02

package plab;

# 現在の日付を文字列で作る
sub getcurrentdatestring
{
	local(@t, $day);
	@t = localtime(time());
	$day = sprintf("%04d/%02d/%02d", $t[5]+1900, $t[4]+1, $t[3]);
	return $day;
}

# 1970年1月1日からの経過日数を得る
sub getcurrentdateint
{
	local $t;
	$t = time() + 60*60*9; # 日本時間に直します。+9時間
	return int($t / (60*60*24));
}

# ログローテートした過去ログファイル名の作成
# 引数(ファイル名, ログ番号)
sub getlogfname
{
	local ($fname, $logno) = @_;
	local($title, $ext);

	if (! $logno || $logno == 0) {
		return $fname;
	}

	($title, $ext) = split('\.', $fname);
	return $title . "." . $logno . "." . $ext;
}

# ログローテート
# 引数(ファイル名, 保存するログ数)
sub rogrotate
{
	local ($fname, $nlog) = @_;
	local $i;

	for ($i = $nlog - 1; $i >= 0; --$i) {
		$i1 = $i;
		$i2 = $i + 1;
		$fname1 = getlogfname($fname, $i1);
		$fname2 = getlogfname($fname, $i2);
		unlink($fname2);
		rename($fname1, $fname2);
	}
}

1;
getformdata.pl
# v 1.01

package plab;

# フォームデータを連想配列に格納します
sub getformdata
{
	local $rawdata;
	local %formdata;
	local @inputs;
	local($input, $name, $val);

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

	@inputs = split('&', $rawdata);

	foreach $input (@inputs) {
		($name, $val) = split('=', $input);
		$name =~ tr/+/ /;
		$val  =~ tr/+/ /;
		$name =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
		$val  =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
		$formdata{$name} = $val;
	}

	return %formdata;
}

# 連想配列をGETメソッド用クエリ文字列にします
sub formdata2query
{
	local $n = @_;
	local $i;
	local $query;

	for ($i = 0; $i < $n; $i += 2) {
		$query .= "$_[$i]=$_[$i+1]";
		if ($i + 2 < $n) {
			$query .= "&";
		}
	}
	return $query;
}

1;

実行結果

例によって、IFRAME タグを使って次のようにウェブページに仕込みます。 サイズは1x1で最小にして見えないようにしています。

<iframe src=pvcounter.cgi width=1 height=1 frameborder=0></iframe>

このページでも、ここにカウンターを仕込んでます(見えません) →

例によって、pvcounter.cgi を仕込んだページをいくつか作りました。

pvcounter.cgiを仕込んだページ その1
pvcounter.cgiを仕込んだページ その2
pvcounter.cgiを仕込んだページ その3
pvcounter.cgiを仕込んだページ その4
pvcounter.cgiを仕込んだページ その5

現在の各ページの閲覧回数をグラフで表示するにはこちらをクリックしてください。

現在の閲覧回数グラフ

解説

プログラムの解説

今までに作ってきたいくつかのライブラリも変更しました。 引数とかの仕様変更をしたライブラリもあるし、 新しい関数を追加したライブラリもありますが 説明したいと思います。

pvcounter.cgi

新しく、保存するログの数、ログを回転する日数間隔が必要になります。 これらをカウントする本体 plab::countpageview やグラフ表示の plab::printgraphwithhref_counters に 渡します。 あと、グラフ表示に関して、現在のグラフを表示するのか過去のグラフを表示するのかっていう 指定ができるように、CGIプログラムのURLの後ろに ?〜 でその辺の指定ができるように変更しました。 ?〜 のところは plab::getformdata で取得しています。 なにも指定していなければカウンタとして動作し、mode=graph&log=ログ番号 を指定すると グラフを表示します。ログ番号を指定しなかった場合は現在のグラフになります。

pvcounter.pl

ページビューカウンター本体ですが、plab::incl_counters を呼び出すだけなので変更はあんまりありません。 CGIプログラムから渡されたログ番号とかのデータをそのまま plab::incl_counters に渡すようにしただけですね。

counters.pl

ここが一番変わったところです。

まず incl_counters 関数について。 予定通り、連想配列の中に、記録を始めた日付を _STARTDATE に、 最後にデータを記録した日付を _ENDDATE に、 それに、経過日数をチェックするため、記録を始めた日付に対応する1970年1月1日からの経過日数を _STARTDATEINT に、 それぞれ入れています。 1970年1月1日からの経過日数を得る関数は plab::getcurrentdateint 関数として stdplab.pl ファイルに入れました。 指定した日数経過したときのファイル名の変更処理は、plab::rogrotate 関数として stdplab.pl ファイルに入れました。 あと、リセット処理ですが、過去のデータを全部クリアするっていうことをするために undef(%counters) ということをしています。 undef というのは変数を初期化するというか、未使用の状態に戻すという関数だそうです。 ですので、undef(%counters) とすると、連想配列 %counters が未使用の状態に戻る、 すなわちデータが全部削除されてクリアされるわけですね。

結構注意が必要だったところは、処理の順番です。 過去ログ処理をするタイミングとカウントアップするタイミングの順番とか。 これを間違えると、正しい処理にならないですもんね。 ログ処理は、日付が変わったときに行われますので、 この時のカウントアップはその日の最初のカウントアップになります。 カウントアップしてから過去ログ処理をしたりすると、間違いです。

あとグラフ表示の printgraphwithhref_counters です。

順を追って説明すると、ファイル名とログ番号から、処理するファイル名を調べます。 これは getlogfname 関数で、stdplab.pl に入っています。 そしてデータを読み込み、内部データ(URLでないデータ)を変数にコピーして、 そのデータを連想配列から削除します。 連想配列からデータを削除するには delete 関数を使います。 そしてランキングの算出を行って(ここは変更無し)、HTMLを出力します。 ここで今回、過去ログを見るためのリンクを出力するように変更したんですが、ちょっと頭を使いました。

まず、過去ログファイルが存在しているかどうかを調べます。 CGIプログラムを設置した直後はもちろん過去ログファイルは存在していませんし、 存在しない過去ログファイルを指定してなにも表示されなかったら、 CGIプログラムが壊れてるのかと思っちゃいますし。 ちゃんとメッセージを表示すればいいわけですが、 最初から、存在しないログファイルを指定できないようにした方がいいですよね。 で、ファイルが存在しているかどうかを確認する方法ですが、if (-e ファイル名) 〜 とします。 ファイルが存在していれば、〜 の部分が実行されます。

次に、リンクのURLは、自分自身なのですが、自分自身といってもこの関数はライブラリの中の関数ですから CGIプログラムのURLなんて知らないですよね。 それで、どうしようか調べたところ、どうやら環境変数 SCRIPT_NAME にCGIプログラムの URLのホスト名を除いた部分が入っているらしいっていうことが分かって、 これを利用することにしました。 そして、与えるデータ、?〜 のところです。 ログ番号を ?〜 に入れてあげないといけません。 しかも、グラフ表示するときは mode=graph っていうのを付けないといけません。 さらに、この関数はライブラリの中の関数ですので、 CGIプログラムはもしかして他の、例えば action=gogo とかなんか未知のデータを使っているかも知れないです。 ですので、現在の ?〜 の内容のログ番号の部分だけを変更した ?〜 を作らないといけません。 で、どうしたかというと、getformdata 関数でフォームデータを全て取得して(?〜 のところはGETメソッド扱いで getformdata 関数で取得できるんでした)、log というヤツだけを書き換えて、 再び ?〜 の 〜 の部分に戻して(plab::formdata2query という関数を作りました。stdplab.pl の中)、 そのURLでリンクするっていうようにしました。 なんとか、うまく動いたので嬉しかったですね。 あとは、そのグラフのデータ記録期間を 日付〜日付 という形で画面に表示するようにしています。

stdplab.pl

新しく追加した関数は、1970年1月1日からの経過日数を得る getcurrentdateint 関数、 過去ログを回転する rogrotate 関数、rogrotate 関数によって過去ログになったファイルのファイル名を 得る getlogfname 関数の3つです。

過去ログファイルは1つだけじゃなくて複数ありますので、 現在のファイルを過去ログにする際には、過去ログを1つずつずらしていく必要があります。 現在のファイル→過去ログ1、とする場合には、同時に、過去ログ1→過去ログ2、過去ログ2→過去ログ3、 というように。こういう処理をログを回転させる、ログをローテートする、なんていいます。 新しく作った rogrotate 関数はこの処理を行います。 実際の仕組みは、ファイルを削除する unlink 関数と、ファイル名を変更する rename 関数を使っています。 注意しないといけないのは、処理する順番。古いログファイルから順番に処理します。 現在のファイル→過去ログ1、過去ログ1→過去ログ2、という順番で処理すると、 全部 "現在のファイル" になっちゃいますから。 for 文の中の --$i というのは $i を1つ減らすという意味です。++$i の逆ですね。

getformdata.pl

formdata2query という関数を追加しました。 フォームデータをGETメソッド形式の ?〜 に戻す関数ですね。

動きました

ばっちりです! だんだん複雑になってきましたが、ちゃんと動きました。 過去ログがちゃんと保存されるか、といったことを確認するために 強制的にデータファイルの日付部分を変更したりして、動作確認しました。 だんだん、実用に耐えるCGIプログラムになってきましたね? …まだまだかな?

分かったこと

  1. データを初期化(未定義に戻す)には undef(変数) とします。
  2. 連想配列から特定のデータを削除するには delete(連想配列{キー}) というようにします。
  3. 自分自身のURLのホスト名を除いた部分が環境変数 SCRIPT_NAME に入っています。?〜 は付きません。
  4. ファイルが存在しているかどうかは -e ファイル名 で調べることができます。
  5. time 関数の戻り値は 1970/1/1 00:00:00 からの経過秒数です。ただしGMTです。
  6. ファイルを削除するには unlink(ファイル名) とします。
  7. ファイル名を変更するには rename(ファイル名, 新しいファイル名) とします。
Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

Copyright (c) 'PERL-LABO' All Rights Reserved.  リンクフリーです。