11. ファイル操作
今日は、ファイル操作について、ご説明いたします。
■ファイル操作の基本
ファイルの読み書きを行うためには、ファイルのオープンとクローズの処理が
基本になります。
open(ファイルハンドル, "ファイル名");
ファイルハンドルというのは、一言で言うと、ファイルを操作するための変数です。
オープン時にファイルハンドルを取得し、読み込みや書き込みも、このファイル
ハンドルに対して行います。
ファイルオープン時には、ファイルを読み込みで開くか、書き込みで開くかを
指定します。ファイル名の部分に、以下の記号で指定します。
open ( FH , " < filename " ); 読込み用
open ( FH , " > filename " ); 書込み用 (上書き)
open ( FH , " >> filename " ); 書込み用 (追加)
open ( FH , " +> filename " ); 読み書き用
open ( FH , " +< filename " ); 読み書き用
ファイルオープン後、ファイル操作が終了したら、必ずファイルをクローズします。
close(ファイルハンドル);
使用例を以下に記述します。
-----------------------------------------------------------------
$filename = "./test.txt";
if (!open(FH, "< $filename")) {
print "ファイルオープンに失敗しました。($filename)";
exit;
}
close(FH);
-----------------------------------------------------------------
open()関数は、ファイルオープンに失敗すると、偽が返ってきますので、
エラー時の処理を書くことができます。
ファイルが存在しない場合や、権限がない場合もエラーとなります。
-----------------------------------------------------------------
※権限について
よくファイル書き込み時につまづくのですが、読み込みや書き込みを行う
ファイルに対して、それぞれ読み込み権限、書き込み権限が必要になります。
WEBサーバがUNIXの場合、ツールやコマンドを使用して権限を変更します。
ファイルが置いてあるディレクトリにも権限がないと、エラーになってしまい
ますので、注意しましょう。Windowsの場合は、読み取り専用になっていると
書き込みができません。
-----------------------------------------------------------------
また、Perlでよく使用されている別の記述方法があります。
-----------------------------------------------------------------
open ( FH , "< $filename ") || die "ファイルを開けません :\n";
-----------------------------------------------------------------
こう記述した場合、open()の結果が偽の場合のみ、die()が実行されます。
if文でもそうですが、"||"を使用した場合、左側の式が真だと、その時点で
全体が真になりますので、右側の式は実行されません。
if (1 || $a = "0") { # 右側の式は実行されない
die()関数は、エラー発生時に、標準出力に第1引数を出力して処理を終了
します。
■ファイルの読み込み
ファイルをオープンした後に、文字列として中身を取り出す方法をご説明します。
$line = <ファイルハンドル>;
ファイルハンドルに対応づけられたファイルの先頭から、1行ずつデータを
読み込みます。1行の区切りとして改行文字が用いられます。通常は、while文で
ループさせます。
-----------------------------------------------------------------
while ($line = <FH>) {
print $line;
}
-----------------------------------------------------------------
上のように記述すると、1回のループで1行取得し、ファイルの最後まで取得
したら、ループを抜けます。
また、
-----------------------------------------------------------------
while (<FH>) {
print $_ . "<br>";
}
-----------------------------------------------------------------
これだけでも、ループで1行ずつファイルの中身を取り出すことができます。
$_で1行ずつデータを取り出します。
@array = <ファイルハンドル>;
ファイルハンドルに対応づけられたファイルの内容を、一括して配列へ代入する
事が出来ます。1行目が $array[0]に、2行目は $array[1]、・・・と格納されて
いきます。
-----------------------------------------------------------------
@array = <FH>;
foreach $tmp (@array) {
print $tmp;
}
-----------------------------------------------------------------
上のように記述するだけで、ファイルの中身を一括で配列に取得します。
read ( ファイルハンドル , 格納変数 , 読み出すバイト長 );
read関数を使用することで、指定したバイト数でファイルデータを取得できます。
-----------------------------------------------------------------
while (<FH>) {
read(FH, $data, 64);
print $data;
}
-----------------------------------------------------------------
上記の場合、ファイルから64バイトずつ、$dataにデータを格納します。
■ファイルの書き込み
ファイルをオープンした後に、ファイルの中身に文字列を書き込む方法をご説明
します。
print ファイルハンドル 出力データ;
ファイルへの出力は通常の print , printf の引数に open で取得したファイル
ハンドルを指定します。それ以外は通常の print文と同じです。
-----------------------------------------------------------------
print FH "File write test.\n";
print FH "This is a file write sample.\n";
-----------------------------------------------------------------
上記のように、ファイルハンドルを第1引数に記述することで、ファイルに
書き込みを行うことができます。
syswrite [ファイルハンドル] , [出力データ] , [出力バイト長];
バイト数を指定して書き込みたい場合は、syswrite関数を使用します。
-----------------------------------------------------------------
syswrite(FH, "File write test.\n", 64);
-----------------------------------------------------------------
指定したバイト数より書き込みデータが多い場合は、データが切り捨てられます。
★今日のまとめサンプルプログラム
-----------------------------------------------------------------
#! /usr/local/bin/perl
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML
$filename = "./test.txt";
if (!open(FH, "< $filename")) {
print "ファイルオープンに失敗しました。($filename)";
exit;
}
@array = <FH>;
foreach (@array) {
print $_ . "<br>";
}
close(FH);
if (!open(FH, "> ./test2.txt")) {
print "ファイルオープンに失敗しました。(./test2.txt)";
exit;
}
print FH "This is a test sample.\n";
close(FH);
print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML
exit;
-----------------------------------------------------------------
■解説
CGIを置いているディレクトリに、適当に中身を書いた"test.txt"を置いて実行して
みてください。
書き込みの方は、ファイルがない場合は、新しく作成しますので、手動で作成する
必要はありません。
CGIを動かして、同じディレクトリに新しく"test2.txt"が作成されていたら、中身も
確認してみてください。
★課題
1. 簡易的なアクセスカウンタを作成してください。
(ヒント)最初は 0 とだけ書いた"count.txt"を作成し、ファイル読み込みを行い、
数字に 1 プラスしたら、それをファイルに上書きし、HTMLにも表示します。
★前回の課題の解答
(1) 'sun;mon;tue;wed;thu;fri;sat'という文字列の";"を除いて配列に格納し、
さらに" : "で区切った文字列に連結し、出力する
(2) 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'という文字列の','を
除いて配列に格納し、アルファベットの逆順にソートする
(3) ('sun','mon','tue','wed','thu','fri','sat')という配列で、最初の'sun'を
一番最後に移動する処理
(4) ('name'=>'Ichiro Suzuki','mail'=>'mail@email.co.jp')というハッシュ配列の
ハッシュ値のみを取り出して出力する
→下のサンプルを参照してください。
-----------------------------------------------------------------
#! /usr/local/bin/perl
print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML
# (1) 'sun;mon;tue;wed;thu;fri;sat'という文字列の";"を除いて配列に格納し、
# さらに" : "で区切った文字列に連結し、出力する
$week = 'sun;mon;tue;wed;thu;fri;sat';
@week = split(/;/, $week);
$week = join(" : ", @week);
print $week . "<br>";
# (2) 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'という文字列の','を
# 除いて配列に格納し、アルファベットの逆順にソートする
$mon = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec';
@mon = split(/\,/, $mon);
@mon = reverse(sort(@mon));
print @mon;
print "<br>";
# (3) ('sun','mon','tue','wed','thu','fri','sat')という配列で、最初の'sun'を
# 一番最後に移動する処理
@week = ('sun','mon','tue','wed','thu','fri','sat');
push(@week, shift(@week));
print @week;
print "<br>";
# (4) ('name'=>'Ichiro Suzuki','mail'=>'mail@email.co.jp')というハッシュ配列の
# ハッシュ値のみを取り出して出力する
%hash = ('name'=>'Ichiro Suzuki','mail'=>'mail@email.co.jp');
foreach $value (values(%hash)) {
print $value . "<br>";
}
print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML
exit;
-----------------------------------------------------------------
■解説
だんだんと難しくなってきましたが、レッスンの内容をちゃんと読んでいれば
できるかと思います。
(1)は、split()とjoin()を使います。
(2)は、split()で配列に格納した後、reverse()とsort()を使用して、逆順にソート
します。
(3)は、shift()、push()の順に使用します。1 行で書いていますが、分けても
結構です。
(4)は、ハッシュ値のみを取り出すので、values()を使用します。
|