- 締切済み
ファイルの読み書きでエラー
下記のようなプログラムでファイルからデータを読み、 データを加工して、ファイルに書き込んでいます。 しかし、頻繁にアクセスがあると、読み出しのエラーや ファイルの中身が消えてしまう症状も発生します。 (ファイルの中身が消えるのは読み出し時にデータが 読めないからかも知れません) エラーをなくす為にはどうしたら良いのでしょうか? 教えて下さい。 $cmdata = file($cmfile); //ファイルからデータを読む ・ ・(データの加工) ・ $fp = @fopen($cmfile,"w+"); flock($fp,LOCK_EX); for( $i=0; $i<count($cmdata); $i++ ) { fputs($fp, $cmdata[$i]); } //ファイルにデータを書く flock($fp,LOCK_UN); fclose($fp);
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- tany180sx
- ベストアンサー率63% (239/379)
> それなら下記の様にデータを2回読んで w+ で追加するだけなら問題なさそうですが もし追加以外のことをするなら一部データをロストすると思います。 2回読む方法でいくなら、 先にループしてデータを作成しておいてから1度に書きこむ、 読み込むときは fread で読み込む。 そもそも読み込んだものを追記している?のは どんなデータを処理をしているんだろ・・
- tany180sx
- ベストアンサー率63% (239/379)
念のため勝手に整理して書いておくと、 flockでロックできるのはflockだけということなので、 (fopenやfread等ができなくなるわけではなく、flockしたときに判断できる) $cmdata = file($cmfile); で、もし中途半端なデータを取得してしまっていたら結局データは壊れてしまいますよね。 読み込み後書き込み可能モードでオープンできればいいですが rwモードってそれとはちっと違う挙動では?
補足
>$cmdata = file($cmfile); >で、もし中途半端なデータを取得してしまっていたら結局データは壊れてしまいますよね。 なるほど、それなら下記の様にデータを2回読んで 2回のデータが同じなら「問題なくデータが読み取れた」と 判断して処理すれば良い訳ですね。 実際、これでやってみて「書き込みエラー(データが 消えてしまう症状)」はなくなりました。 $cmdata1 = file($cmfile); $cmdata2 = file($cmfile); if ( ( $cmdata1 == $cmdata2 ) == true ) { // 正しい時の処理 { else { // データ読み込みを失敗した時の処理 {
- tany180sx
- ベストアンサー率63% (239/379)
> 読み込みファイルをflockした後閉じずに、書き込みオープンしても必ず失敗しません? うちのCent系では一応問題なく動きました。 んで fopen($file, 'rw'); が正常に動作しないんですよねぇ.. MySQLとかじゃなくても、なんらかのDBを使えば排他的処理はやってくれるでしょうから できたらその方がベターかとは思いますけど。
- BellBell
- ベストアンサー率54% (327/598)
$cmdata = file($cmfile) おそらくは、ここでデータを読み込めていないにも関わらず、データ加工した上で書き込みしているために、ファイルの中身が消えてしまっているんですね。 想像すると、他のリクエストで発生しているflockが原因でしょうね。 読み込み、加工、書き込みをセットで、処理しないといけないですね。 読み込みが成功したかどうか確認して、次の処理することですね。 ザクッと書くと、以下みたいな感じですかね。 $cmdata = file($cmfile); if ($cmdata !== false){ (データの加工) $fp = @fopen($cmfile,"w+"); flock($fp,LOCK_EX); for( $i=0; $i<count($cmdata); $i++ ) { fputs($fp, $cmdata[$i]); } //ファイルにデータを書く flock($fp,LOCK_UN); fclose($fp); } できれば読み込みの際にfileではなくて、 @fopen($cmfile,"rw"); として、読み書き可能で開いて直後にflockの方がいいですね。 #1さん 読み込みファイルをflockした後閉じずに、書き込みオープンしても必ず失敗しません? とツッコミ。
補足
有難うございます。 なるほど、「読み取りのエラーを回避する事はできないけど、 エラーを出た事をチェックしておけば、書き込みエラーを 回避できる」という事ですね。 これで試してみて、結果は、また、報告したいと思います。
- tany180sx
- ベストアンサー率63% (239/379)
# flock はたいして使ったことないので間違ってたらすみません。 > $cmdata = file($cmfile); //ファイルからデータを読む file(), file_get_contents() and even fopen($file, ‘r’) will ignore the lock. 書き込みのための読み込み時は fopen で開いてロックかけた方がよいのでは? > flock($fp,LOCK_EX); 念のため返り値でif分岐? こんなんでダメかなぁ? $file = 'test.log'; $fpr = fopen($file, 'r'); if ($fpr && flock($fpr, LOCK_EX)) { $data = fgets($fpr); $data += 1; sleep(2); if ($fpw = fopen($file, 'w')) { fputs($fpw, $data); fclose($fpw); } fclose($fpr); } else { die('開けません。'); } echo $data; マルチプロセスで flock が機能しない?とかなら mkdir で自前 flock とか。
補足
ありがとうございます。色々と試していたので返事が遅くなりました。 >そもそも読み込んだものを追記している?のは >どんなデータを処理をしているんだろ・・ 何十ページもあるサイトのカウンターを1つのファイルで記録しているようなものをイメージして下さい。 だから、「このページにアクセスがあった時は、この数を増やす」という処理が必要となり、どのデータを書き替えるかランダムで「追記」では対応できません。多少、カウントの誤差があっても構わないですが「データが消える」という事態は避けたいです。 前回の「2回読んで同じデータなら」というやり方では読み込みエラーが多く、エラーの回数を減らしたかったので、待ち時間も設定して、何度かデータを読み事にして、次のようなプログラムで試しています。 ************************ ここから ************************ $ok_flag = 0; for( $i=0; $i<50; $i++ ) {usleep(20000); // 2/100sec. $cmdata1 = file($cmfile); $cmdata2 = file($cmfile); if( ($cmdata1 == $cmdata2 ) == true && $cmdata1 !== false ) { $ok_flag = 1; break; } } if ( $ok_flag == 1 ) { // 正しい時の処理 { else { // データ読み込みを失敗した時の処理 { ************************ ここまで ************************ >@fopen($cmfile,"rw"); >として、読み書き可能で開いて直後にflockの方がいいですね。 かなり多くのアクセスがある事が考えられるので、flock の時間は短くしたい所で、「読んで」「処理して」「書き込み」の間、flock をすると、その間、他のアクセスがあった時の処理ができないので避けたいと考えています。 (1)あるアクセスによりデータを読んだ後 (2)他のアクセスで書き替えられ (3)その後、(1)で読んだデータを処理して、データを書き込む という事が起これば、(2)で書き替えられたデータは無効になりますが、それは別に構わないと考えています。 「アクセスが集中してもデータが消えない」という事を優先させ、その次に「多少、待ち時間があってもエラーの処理は避けたい」と考えています。