csvファイルを読み込み→上書きするスクリプトを作ってみましたがうまく行かないときがあり原因を考えています。
100行程度のcsvから任意の行($idで指定したもの)を書き換えたいのですが、どこかに間違いや改善点があればお願いします。
うまく動作しない理由が知りたいです。
<?php
//データの取り出し
$id = $_GET['id'];
$data = file("../data/file.csv");
//idで指定した行に書き込み
$data[$id] ="テスト書き込み";
//csvファイルにデータの書き込み
$file = fopen("data/file.csv","w+");
flock($file, LOCK_EX);
for($i=0; $i<count($data); $i++) {
fwrite($file,$data[$i]);
}
fclose($file);
?>
$data = file("../data/file.csv");
$file = fopen("data/file.csv","w+");
ファイルの場所が違うみたいですけど、
これはいいのでしょうか?
ざっと見る限り、
・上書き処理なのに、file 関数と fopen 関数に指定しているファイル名が違う
・マニュアルには file 関数は LOCK を取得するという表記は無い。同時リクエストがあった時にファイルは壊れる可能性が高い。
ところが疑問に思いました。
すみません、ファイルのアドレスは単純ミスです。
「テスト書き込み」が書き込まれるときとそうでないときがあるんですよね。。。このスクリプトはhttp://php.s3.to/tt/tt4.phpここを見て作りました。lockは間違っているでしょうか、、、
closeの前にunlockが抜けている
flock($file, LOCK_UN);
Lock間の処理時間を短くすればそれなりの要求にも対応させられます。
同時書き込み数件程度であれば問題ないと当方でテスト済み。それ以上であればDBのトランザクションをお使いください。
ありがとうございます。
アンロックつけてみます。
根本的に違ってないようなのでうまく動作するときとしないときの差は、、もしかしたら契約しているサーバがあまりにも安価なところだったためかもしれないですね。
http://testwiki.仮.jp/?PHP%2F%A5%D5%A5%A1%A5%A4%A5%EB%A5%ED%A5%C3%A5%AF%2F%A5%CE%A1%BC%A5%C8#f4a10010
補足:
もっと厳密に更に精度を上げたいのであればバッファを考慮することと、戻り値(flock,fclose)を取得し例外を追加してください。
そこまで精度を上げるなら、尚更DBのほうが良いという結論になってしましますが。。。
バッファ、知りませんでした勉強になります。
$id = $_GET['id'];
$idを添字として使っているので数字かどうかチェック(http://q.hatena.ne.jp/1114406105]is_numericしてis_int)した方がいいでしょう。また代入する前に添字の範囲チェックもした方がいいでしょう。
$data[$id] ="テスト書き込み";
問題はこの代入でしょう。行の識別をfile()によって行っているので改行文字が必要です。これでは次の行と結合してしまい、上書きではなく、行の削除と挿入になってしまいます。
$data[$id] ="テスト書き込み\n"; // osによっては\r\nかも
書き込みはforもいいですがforeachの方が綺麗です。
またimplode()で長い文字列にして書き出すのも手です。
また上書きではなく、別の名前で書き出してオリジナルをリネームし、
書き出したファイルをリネームするとした方が安全です。
まぁ数百行のファイルで書き込めないという状況にはならないと思いますが。
ロックについては書き込みの時点では不十分でしょう。するならロックしてからReadしてWriteするようにしないと厳密にはまずいでしょう。
file()はパスしか受けつけないのでfgets()でfile()のような機能を作らないといけなくなるので面倒ですが。
やはり、
$data[$id] ="テスト書き込み\n";
こちらが動作するときと
指定した$id行が空欄になってしまうときがあります。
スクリプトを改善したので契約サーバに問題があるのでしょうか??
>契約サーバに問題があるのでしょうか??
それは無いと思います。
$idと代入した後の$dataの値を確認してみてはどうでしょう
//idで指定した行に書き込み $data[$id] ="テスト書き込み"."\n"; echo "id=".$id."<br>"; print_r($data);
例えば$id="a"でも動作しますが出力のfwrite($file,$data[$i]);ではアクセスされないので書き込まれません。またdata/file.csvが10行しかないのに$id=11としても書き込まれません。
>こちらが動作するときと指定した$id行が空欄になってしまうときがあります。
data/file.csvの確認の方法はどうしているのでしょう。
更新時間は合っていますか。
ブラウザーでしているのであれば、キャッシュの影響はないのでしょうか。
>ブラウザーでしているのであれば、キャッシュの影響はないのでしょうか。
たとえば20行目に{テスト書き込みを}書き込む様に命令したとしても、
19行目
21行目
のように{テスト書き込み}が書き込まれるべきところが抜け落ちてCSVファイルが書き込まれてしまいます。(毎回ではなくたまにですが)
原因はなにか見当がつきません、、、
本来ならば、
19行目
20行目 テスト書き込み
21行目
スクリプトが正常に動けば上記のように書き込まれるはずなのですが、、、
解決策は無いでしょうか、、、、
>たとえば20行目に{テスト書き込みを}書き込む様に命令したとしても、
>19行目
>21行目
>のように{テスト書き込み}が書き込まれるべきところが抜け落ちてCSVファイルが書き込まれてしまいます。
確認ですが最初CSVファイルが
19行目 A 20行目 B 21行目 C
あった時、
19行目 A 20行目 C
と言うように最初20行目にあったものが抜けたようにたまになるということでしょうか。
CSVファイルは行の連続で19行目の次は21行目とはならないのですが
いずれにしろ抜けるためには
unset($data[$id]);
$data[$id] = NULL;
$data[$id] = "";
としなければならずうなづけないのですが。
最後の行が抜けるというのであれば$idによっては考えられますが。
違うphp scriptが同じCSVファイルに対して書き込んでいるということはありませんよねぇ。
そのためにも
//idで指定した行に書き込み $data[$id] ="テスト書き込み"."\n"; echo "id=".$id."<br>"; print_r($data);
をしたら確認できると思うのですが。また再現性を確保するために違うファイルに書き出してみてどうでしょう。
またこのファイル(phpの方)はhanabusatsukasaさん以外の人も随時アクセスできる状態なのでしょうか。そうであれば同時に読み込みがあった場合、どちらかのアクセスが反映されないということは考えられますが抜ける原因にはならないですけど。
すみませんこれは単純ミスです。
「テスト書き込み」が書き込まれるときとそうでないときがあるんですよね。。。