微妙にうまくいかない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); // ファイルをクローズ
?>

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2010/05/27 17:40:02
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:doropon No.1

回答回数94ベストアンサー獲得回数16

ポイント20pt

こんばんは。

処理ですが、

ファイルを$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); // ファイルをクローズ
?>
id:goodbabies

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

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

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

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

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

2010/05/21 10:04:41

その他の回答4件)

id:doropon No.1

回答回数94ベストアンサー獲得回数16ここでベストアンサー

ポイント20pt

こんばんは。

処理ですが、

ファイルを$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); // ファイルをクローズ
?>
id:goodbabies

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

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

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

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

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

2010/05/21 10:04:41
id:haruchaco No.2

回答回数1ベストアンサー獲得回数0

ポイント20pt

入力されたメールアドレスを配列に入れていないので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 "ファイル書き込みオープン失敗";

}

}

?>

id:goodbabies

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

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

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

2010/05/21 10:06:50
id:gekikawa No.3

回答回数110ベストアンサー獲得回数11

ポイント20pt

重複してしまう原因は、フォームから入力されたメールアドレス( = $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()あたりを参考にしてみて下さい。

id:goodbabies

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

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

↑↑↑↑

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

2010/05/21 10:08:49
id:taramonera No.4

回答回数79ベストアンサー獲得回数5

ポイント20pt

「すでに登録済みです」とメッセージを出すのであれば、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);
        }
?>
id:goodbabies

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

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

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

2010/05/21 10:10:08
id:koriki-kozou No.5

回答回数480ベストアンサー獲得回数79

ポイント20pt

処理の流れをみると判る


(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

となる

id:goodbabies

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

2010/05/21 10:12:25

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません