ランダムアクセスファイルについて

[HOME] [BACK] [NEXT]

発言保存用ランダムアクセスファイルへのアクセスについて 前のページ同様シンプル掲示板のソースコードを引用しながらランダムアクセスファイルの使い方について説明します。
発言保管方法は、発言があるたびに発言保存ファイルに保存していき、iniファイルに設定してある最大保存発言数に達したら次の発言から新しいファイルに保存します。そしてiniファイルで設定してある最大ファイル数に達したら次からは古いファイル順に上書きするというものです。

この発言保存ファイルはランダムアクセスファイルを使っています。ランダムアクセスファイルとはシーケンシャル形式ではなくて 固定長のデータの集合であるレコードを単位として保存されているファイルの事です。
perlではランダムアクセスファイルのread/writeのために、発言者、発言、時刻といったデータを1レコードとしてまとめ、 レコード毎にランダムアクセスファイルにかき出したり読み出したりする(固定長ランダムアクセス)ための関数が 用意されています。これを使うと、管理が面倒な、1発言に付き1つのファイルに保存するという方法を取らなくても、発言レコードのread/writeが高速にできます。

1.レコード情報定義

まず初期設定部で、「ファイル内レコード番号、時刻、発言者名、発言、E-Mailアドレス、URL」の6個のデータを 1つのレコードとするためのレコード情報とレコード長を変数に格納します。

$dat="A20 A50 A30 A500 A70 A70"; #レコード情報   
$dat_len=length(pack($dat)); #ファイルに書き込むレコード長   

ここで「A20」はフォーマット制御文字列で、20文字(1文字1バイト換算で20バイト)という意味です。つまりレコードは 頭から「20文字、50文字、30文字、500文字、70文字、70文字」の計740文字(バイト)ですよというレコード情報 を表す文字列を$datに代入しています。
次にこの1レコードの長さを求めて$dat_lenに代入しています。pack関数は通常引数に、
(フォーマット制御文字列,data1,data2,...)というリストを指定して、組み立てた文字列を返しますが、ここでは データの部分を省略してファーマット制御文字列($dat)のみ渡しています。これでもlength関数でその長さを 計算してくれます。

2.レコードの書き出し

POSTで送られてきた発言等のデータをReadParseサブルーチンでデコード処理した後、各変数に格納します。
次に、専用のポインターファイル(テキストファイル)から読んだ発言ポインターが最大値であったなら以下の様に 次の発言保存ファイルを上書きモードでオープン

open DATFILE,">$datfile0" or die "Cannot Open $datfile0 :$!";   
#上書きモードでオープン   
flock DATFILE, $EX_LOCK;

発言ポインターが最大値になっていなかったら、ファイルポインターが現在指しているファイルを追加モードでオープン

open DATFILE,">>$datfile0" or die "Cannot Open $datfile0 :$!";   
#追加モードでオープン   
flock DATFILE, $EX_LOCK;

$datfileには適宜ファイル名を代入しておきます。次に1レコード分をデータとして保存します。

$i=0;#オフセットレコード数   
seek(DATFILE,$i*$dat_len,1);**   
print DATFILE pack($dat,$pvalue,$zikan,$nname,$hhatsugen,$eem,$hhp);   
flock DATFILE, $UNLOCK;   
close DATFILE;

「$pvalue,$zikan,$nname,$hhatsugen,$eem,$hhp」にはそれぞれ
「ファイル内レコード番号、時刻、発言者名、発言、E-Mailアドレス、URL」を代入しておきます。
seek関数は ファイルハンドル名で指定されたファイルへのアクセスポインターを指定位置にセットします。 引数の「1」はファイルの末尾から数えることを示し、 「$i$dat_len(=0)」でオフセット0を指定していますから この場合常にファイルの最後(末尾から1番目のレコード位置)にセットしていることになります。*
(上書きモードや追加モードでオープンしたファイルに対してはseek関数は無効となります。 上書きモードでは常にファイルの最初のレコードから、追加モードでは最後のレコードの次に書き込まれます。 また、引数の「1」はファイルの末尾からではなく現在の位置から数えることを表します。)
次にpack関数を使って事前に定義してあるレコード情報を表すフォーマット制御文字列に対応した固定長 レコードを生成しています。この生成したレコード長は740バイトです。
最後の3行で、ファイルアクセスポインターがセットされている位置に**print文で書き出してロックを解除し、クローズします。

3.レコードの読み出し

GETで起動されると、最新の発言保存ファイルから新しい順に読み出してその都度標準出力にprintします。 以下は読み出し部のソースです。

open DATFILE,"<$datfile0" or die "Cannot Open $datfile0 :$!";   #読み込み用   
flock DATFILE, $EX_LOCK;   
$st=$pvalue;   
$en=1;   
for ($i=$st;$i>=$en;$i--){   
 seek(DATFILE,($i-1)*$dat_len,0);   
 $r_count=read(DATFILE,$r_buf,$dat_len);   
 ($NN,$zikan,$nname,$hhatsugen,$eem,$hhp)=unpack($dat,$r_buf);   
 .......(レコード内の各データを標準出力へprintする)........    
}    
flock DATFILE, $UNLOCK;    
close DATFILE; 

for文で、読み出す発言を指すポインターをデクリメントしながら、最新のレコードからファイル内で 最も古い1番レコードまで、順番に読み出してその都度標準出力へprintします。
ここのseek関数への引数として「0」はファイルの先頭から数える事を表し、 「($i-1)*$datlen」で$i番レコードのオフセットを指定していますから、ファイルの先頭から数えて$i番目のレコードに アクセスポインターをセットしている事になります。 この$iをデクリメントしていくとファイルの先頭方向、つまりより古いレコードへアクセスポインターがずれて行きます。
read関数を使って$dat
lenで示される長さのデータ(1レコード分)を$r_bufに読み込んだ後、 unpack関数で6個のデータに分解しています。

[HOME] [BACK] [NEXT]

更新日: