プロが教えるわが家の防犯対策術!

メールアドレスを登録してもらったり削除してもらったりするプログラムです。
.datファイルに登録されたメールアドレスが1行づつ入ってます(改行付き)
重複チェックをする部分がよくわかりません。
チェックしたいメールアドレスを$mailに入れてます。

登録時、重複してないかチェックする時に
.datファイルを@allbodyに読み込んで
foreach $line (@allbody){
if ($line =~ /$email/){
&error("そのメールアドレスは登録済みです");
}
とすると出来てるようです。

これが削除時の場合
foreach $line (@allbody){
if (!($line =~ /$email/)){
&error("そのメールアドレスは登録されてません");
}
とすると出来ません。
登録されてようがされてなかろうが、登録されてません、と表示されます。

そもそも登録時の時できてると思ってるのが間違いなんでしょうか?
どうすればうまくいくか教えて下さい。

A 回答 (4件)

> よくわからないのが「@allbodyの全要素と比較すれば一致しないことがあるのは当たり前」という事です。


なんでなんでしょう??

説明がちょっと簡潔過ぎましたね。

ある値が配列に「含まれていない」ことを確認するには、その値を配列の全要素と比較し、どの要素とも一致しないことを調べる必要があります。しかし、質問で書かれた方法は、ある要素と一致しなかった時点で「含まれていない」と判断してしまっているのです。

例えば、登録済みのアドレスが「abc@example.com」と「def@example.com」の2件あったとしましょう(この2件が @allbody に入ります)。また、そのうちの abc@ の方を削除するものとしましょう(abc@ のアドレスが $email に入ります)。

まず、foreach $line (@allbody){ によって $line に abc@example.com が入ります。次に if (!($line =~ /$email/)){ で比較しますが、$line と $email は同じ値なのでエラーにはなりません。

しかし、ループの2回目では $line に def@example.com が入ります。もちろん、$email とは一致しないので、&error が実行されてしまいます。

せっかくループの1回目で「含まれていること」を確認できたのに、これでは元も子もありません。また、削除したいのが def@ の方だったなら1回目でエラーとなってしまうので、含まれていることすら確認できません。

このように、ループ内の条件判断だけで「含まれていないこと」を調べることはできないのです。しかし、逆の「含まれていること」を調べるにはそれで十分なので、No.2 で示した方法ではそれを利用し、含まれていないこと(!含まれている)を調べたのです。

ちなみに、No.3 で BLUEPIXY さんが書かれている「ハッシュ」を使うと、No.2 の is_registered は次の用に書き換えられます。

sub is_registered {
  my $test = shift;
  my %entry = map {$_=>1} @_;
  return $entry{$test};
}

アルゴリズムに関する質問だと思ったので敢えて書きはしなかったのですが、確かにこの方法の方が簡単ですね。

この回答への補足

大丈夫です。ちゃんと理解しましたよ。
(下のお礼だと、なんか突飛な事書いてるんで‥‥一応フォローを)

補足日時:2005/06/12 15:34
    • good
    • 0
この回答へのお礼

再度の回答ありがとうございます。
何度も何度も読んでやっと良く理解できました。
どうも私はforeachの動きをよく理解してなかったようです。(ってかforeachの中にif(‥を入れた場合の動きを理解してなかったのかな)
foreach $line (@allbody){
print "$line"
}
とすれば@allbody全てプリントしてくれるって本に書いてあったので、そこで脳がストップしてたみたいです。
私の脳に足りないのはフラグです。以前にもご教授いただいたんですがフラグ。もっとうまくフラグを使えるよう(思いつけるよう)頑張ります。
わかりやすい説明をありがとうございました。%まで‥‥。
お時間をさいていだたいた分、頑張りたいと思います。

お礼日時:2005/06/12 15:26

このような処理をする場合は、ハッシュを使うのが簡単だと思います。

    • good
    • 0
この回答へのお礼

%ですか‥‥
実は苦手で一度も使った事ないんです‥‥難しそうでしょう。
でも苦手とか言ってる場合じゃないですよねぇ。
アドバイスをもらって、私程度がやりたい事でも%を使えば簡単にできるという事がわかったので、これを機に勉強してみよう(かなぁ‥‥)と思ってみました。
アドバイスありがとうございました。

お礼日時:2005/06/12 15:34

まず、メールアドレスのような完全形のチェックが必要なものに、パターンマッチを使うのはやめましょう。

例えば、「hoge@example.com」というアドレスで登録しようとした時、それを「含む」アドレス(例えば「fuga-hoge@example.com」など)が既に登録されていると、登録済みと判断されてしまいます。

もちろん、そのようなことがないように正規表現を書けば済むのですが、Perl には文字列の一致、不一致を調べる eq や ne があるので、普通はそちらを使います。

本題ですが、削除時のチェックがうまく行かないのは、全てのアドレスと比較してしまっているからです。もし、$email がちゃんと登録されているアドレスだったとしても、@allbody の全要素と比較すれば「一致しないことがある」のは当たり前です。

このような場合はループの途中で判断しようとせず、ループとフラグを使って「登録されていること」を調べるようにします。

$registered = 0;
foreach $line (@allbody) {
  if ($line eq $email) {
    $registered = 1;
    last;   # 登録されているのが分かったので、これ以上調べなくてもよい。
  }
}
if (!$registered) {
  error("そのメールアドレスは登録されてません");
}

また、登録時にも同じことを調べているので、これを 「$registered を返す関数」として定義し、両方で使い回すとよいでしょう。

sub is_registered {
  my ($test, @entry) = @_;
  foreach my $item (@entry) {
    if ($test eq $item) {
      return 1;
    }
  }
  return 0;
}

# 登録時の重複チェック
if (is_registered($email, @allbody)) {
  error("そのメールアドレスは登録済みです");
}


# 削除時の登録チェック
if (! is_registered($email, @allbody)) {
  error("そのメールアドレスは登録されてません");
}

※インデントに全角空白を使っているので、コピーする場合はタブなどに置換して下さい。
    • good
    • 0
この回答へのお礼

さっそくの回答ありがとうございます。
とてもわかりやすい説明うれしく思います。
パターンマッチを使ってはいけない理由など、とても納得しました。

よくわからないのが「@allbodyの全要素と比較すれば一致しないことがあるのは当たり前」という事です。
なんでなんでしょう??
一致した時点で一度ループを抜ければ良いのかなぁ、というのはわかりました。でもその理由がわからないのです。
よろしければ、もう一度そのあたり教えていただけませんか。

お礼日時:2005/06/11 00:43

if ($line =!~ /$email/){



これでできませんか?
    • good
    • 0
この回答へのお礼

!~なのかな。=~の反対ですよね。
やってみたんですができなかったんですよ。
回答していただき、ありがとうございました。

お礼日時:2005/06/12 15:30

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!