人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

微妙にうまくいかないPHP 配列の重複削除

いつもお世話になってます。

フォームからメールアドレスを入力して、ログファイルに書き込みする簡易プログラムを作りました。
重複させたくないので、array_unique関数を使って、重複した配列を削除させることにしましたが、最新で重複したもの2件だけは残ってしまいます。(意味分かりますかね?)2件だけ必ず重複が残ってしまうのです。そこから何回同じ内容を送信しても、2件以上重複になることはありません。

あと、あわよくば、『すでに登録済です』とメッセージも出せたら最高なのですが・・・。
【スクリプト】
<?php
$mail=$_POST['mail'];
$string = $mail."\n";
$lines = file("mail.log");
$fp = fopen("mail.log", "w"); // ファイルを書き込み用にオープン
fputs($fp, $string);
for($i = 0; $i < count($lines); $i++){
$lines2 = array_unique($lines);
fputs($fp, $lines2[$i]);
}
$fp = fclose($fp); // ファイルをクローズ
?>

●質問者: goodbabies
●カテゴリ:コンピュータ ウェブ制作
✍キーワード:FP PHP String オープン クローズ
○ 状態 :終了
└ 回答数 : 5/5件

▽最新の回答へ

1 ●
●20ポイント ベストアンサー

こんばんは。

処理ですが、

ファイルを$linesに全部読み込む

ファイルに新しいアドレスを書き込む

$linesの行数を元に値を比較し書きこむ

問題点

1.$linesには新しく書きこんだアドレスが入って無い。

なので、重複したデータが必ず先頭行に残る。はず。

2.array_uniqをforでまわす根拠がない。一度でいいです。

使っている関数を同じ感じだと、こんなんです。

新しいアドレスをaで追加書き込み

一応閉じてからもう一度開いて、

uniqe

で書き込み

一行ずつ書きこむのにforでやっていますが、

wrapされているfile_put_contentsでやったほうが見た目が多少いいかと思います。

<?php
$mail=$_POST['mail'];
$string = $mail."\n";

$fp = fopen("mail.log", "a"); // ファイルを追加書き込み用にオープン
fputs($fp, $string);
$fp = fclose($fp); // ファイルをクローズ

$lines = file("mail.log");
$fp = fopen("mail.log", "w"); // ファイルを書き込み用にオープン
$lines2 = array_unique($lines);

for($i = 0; $i < count($lines2); $i++){
fputs($fp, $lines2[$i]);
}
$fp = fclose($fp); // ファイルをクローズ
?>
◎質問者からの返答

うわ??!そうだったのですね。

問題点を指摘され、『そうだったのか?!』と、叫んでしまいました。

まだPHP初心者のため、大変参考になりました。感動です。

もし、お手数でなければ、file_put_contentsをなぜ使ったほうがよいのかがいまいち分かりません。

教えていただけると幸いです。


2 ● haruchaco
●20ポイント

入力されたメールアドレスを配列に入れていないのでarray_unique関数では消せません。

改行削除モードでfile関数で開きin_array関数を使えばarray_uniqueは使わなくても重複しません。

<?php

$mail= trim(stripslashes($_POST['mail']));

$lines= file('mail.log', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

if( in_array($lines,$mail) ) {

echo "そのアドレスは既に登録済みです";

} else {

array_unshift($lines,$mail);

$lines = array_unique($lines);

$fp = @fopen('mail.log','w');

if( $fp ) {

if(flock($fp, LOCK_EX)){

foreach( $lines as $line ) {

fwrite($fp,$line."\n");

}

flock($fp,LOCK_UN);

} else {

echo "ファイルロック失敗";

}

$fp = fclose($fp);

} else {

echo "ファイル書き込みオープン失敗";

}

}

?>

◎質問者からの返答

>改行削除モードでfile関数で開きin_array関数を使えばarray_uniqueは使わなくても重複しません

なるほど、そうなのですね!参考になります。

エラー処理まで書いて頂いて、ありがとうございました!


3 ● gekikawa
●20ポイント

重複してしまう原因は、フォームから入力されたメールアドレス( = $string)が

それまで蓄積したメールアドレス( = $lines)と重複しているかチェックしていない為です。


mail.logには重複を許さない条件で保存しているので、ここから配列($lines)を作り

この配列に対してarray_uniqueを使うのはややもったいない感じです。

(個人的にはarray_uniqueはゴチャ混ぜのところから、重複を取り除く時に使いやすいものと感じます。)


で、私だったらということですが

$mail=$_POST['mail'];

$string = $mail."\n";

$lines = file("mail.log");

if(in_array($string, $lines)){

echo "すでに登録済です";

}else{

$lines[] = $string;

$tmp = implode('', $lines);

$fp = fopen("mail.log", "w"); // ファイルを書き込み用にオープン

fputs($fp, $tmp);

$fp = fclose($fp); // ファイルをクローズ

}

?>

という所でしょうか?

あと、このままですとフォームからデータを送信するタイミングによって

データが壊れる可能性があります。

具体的にはfopen()?fclose()の間に、file()が生じると、

$linesが正しく全てのデータを読み込めていないことになります。

複数の人がほぼ同時にこのフォームを使う事を考慮する必要があれば工夫が必要という事です。

flock()あたりを参考にしてみて下さい。

◎質問者からの返答

>複数の人がほぼ同時にこのフォームを使う事を考慮する必要があれば工夫が必要という事です。

flock()あたりを参考にしてみて下さい。

↑↑↑↑

なるほど、そこは盲点でした。そのことを全く考えていませんでした。さっそくやってみようとおもいます。


4 ● taramonera
●20ポイント

「すでに登録済みです」とメッセージを出すのであれば、array_unique()はいらないですね。

<?php
 $mail=$_POST['mail'];
 $string = $mail."\n";

 $lines = file("mail.log");

 if( in_array($string, $lines, true) ){
 print "すでに登録済です";
 }else{
 array_unshift($lines, $string);

 $fp = fopen("mail.log", "w");

 for($i = 0; $i < count($lines); $i++){
 fputs($fp, $lines[$i]);
 }

 $fp = fclose($fp);
 }
?>
◎質問者からの返答

>「すでに登録済みです」とメッセージを出すのであれば、array_unique()はいらないですね。

え?!いらないんですか!

でもとても参考になります。ありがとうございます。コードもわかりやすかったです


5 ● koriki-kozou
●20ポイント

処理の流れをみると判る


(1)ファイルから読み込み

$lines = file("mail.log");

(2)ファイルへ追加

$fp = fopen("mail.log", "w"); // ファイルを書き込み用にオープン

fputs($fp, $string);

(3)重複削除

$lines2 = array_unique($lines);


(3)の時点で相手にしている$linesは (1)の時点でのファイルの内容だから(2)で追加した分は含まれていない

だから必ず同一のもの2つが存在可能となっている


本来の流れとしては

(1)ファイルから読み込み

(2)既存かどうかの確認

array-searchという関数で $lines に $string が含まれているかどうかを確認

http://jp.php.net/manual/en/function.array-search.php

(3a)含まれていなければファイルへ追加して「登録しました」をecho

(3b)既存であれば「すでに登録済です」をecho

となる

◎質問者からの返答

なるほど、最初に処理の流れを考えなければいけないのですよね、痛感しました。とても参考になりました、ありがとうございます!

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ