CGIの質問です。以下は携帯用メール送信フォームCGI(http://www.deaikensaku.net/o1.shtml)の一部です。

フォームに入力された内容をCSVに書き込みます。次の2つの条件によってCSVに書き込む内容を分岐するにはどこを修正すればよいでしょうか?お智恵をお借りできれば幸いです。
1.メルマガ($newsletter)の値が"希望する"または"登録済み"の場合で
2.docomo,ezweb,softbank,vodafoneを含む携帯アドレスならmail_keitai.csvにそれ以外はmail.csvに書き込む
※これはhttp://q.hatena.ne.jp/1203118562#a802935の派生質問です。アドレスがすでにCSVに存在する場合は2つの条件が当てはまっても書き込みません。

$csv = "1";
$file = "./mail.csv";
$file2 = "./mail_keitai.csv";
$lock = "./lock/mail.dat";
$mail = $FORM{'mail'};
$name = $FORM{'name'};
$newsletter = $FORM{'newsletter'};
$mail2 = $FORM{'mail2'};

#書きこむ
open(FILE, "<$file") or &err2('エラーです。');
@lines= <FILE>;
close(FILE);
for($x=0;$x <= $#lines;$x++){
my @HashTmp = split("\,",$lines[$x]);
$Hname{$HashTmp[2]} = $HashTmp[1];
}
unless(exists($Hname{$mail})){
unshift(@lines,"$name,$mail\n");
open(OUT, "+<$file") or &err2();
print OUT @lines;
close(OUT);
}

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

ベストアンサー

id:kiryuu No.2

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

ポイント400pt

まず条件を整理します。

1)$newsletterが「希望する」又は「登録する」の場合は書き込み処理を行う。

2)$mailにdocomo,ezweb,softbank,vodafoneのいずれかが含まれれば$file2に書き込み、当てはまらなければ$fileに書き込む。

1の条件式は、($newsletter eq '希望する' || $newsletter eq '登録する') となります。

2で真を返す正規表現は、($mail =~ /(docomo|ezweb|softbank|vodafone)/) となります。

他の方は$fileを上書きする方法を採られていますが、私は専用の変数(この場合$wfile)を使用する方が好きです。(単に好みの問題です)

$csv = "1";
$file = "./mail.csv";
$file2 = "./mail_keitai.csv";
$lock = "./lock/mail.dat";
$mail = $FORM{'mail'};
$name = $FORM{'name'};
$newsletter = $FORM{'newsletter'};
$mail2 = $FORM{'mail2'};

#書きこむ
#newsletterの値が'希望する'か'登録する'ならば実行する。
#それ以外なら何もしない

if($newsletter eq '希望する' || $newsletter eq '登録する'){

	#メールアドレス内文字列の判定を行う
	my $wfile;	# $wfileを局所化します。他で使っていなければやらなくてもOK
	if($mail =~ /(docomo|ezweb|softbank|vodafone)/){
		$wfile = $file2;
		}else{
		$wfile = $file;
		}

	open(FILE, "<$wfile") or &err2('エラーです。');
	@lines= <FILE>;
	close(FILE);

	for($x=0;$x <= $#lines;$x++){
		my @HashTmp = split("\,",$lines[$x]);
		$Hname{$HashTmp[2]} = $HashTmp[1]; 
		}

	unless(exists($Hname{$mail})){
		unshift(@lines,"$name,$mail\n");
		open(OUT, "+<$wfile") or &err2();
		print OUT @lines;
		close(OUT);
		}

	}

 ただ、質問文を読んでみると携帯電話用のアドレスとそれ以外のアドレスを別々のファイルで管理したいかの様に思えます。

 ($mail =~ /(docomo|ezweb|softbank|vodafone)/) ですと、例えば「docomonsense@hatena.ne.jp」(DoCommonSenseのミススペル)も「携帯電話アドレス」と判定されます。

 Docomoのメールアドレスは、~@docomo.ne.jp

 ezwebのメールアドレスは、~@ezweb.ne.jp

 SoftBankのメールアドレスは、~@softbank.ne.jp

 Vodafoneのメールアドレスは、~@*.vodafone.ne.jp

ですので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)/)

が適切かと。

 更に、これらのドメインは末尾にくるはずなので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/)

この場合の//内の「$」は、文字列の末尾を表します。

 ついでに、ドメインネームについては大文字小文字を区別しませんので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/i)

//の外の「i」は、大文字小文字を区別しないパターンマッチを行う修飾子です。


 これをメールアドレス内文字列の判定に適用すると、

	#メールアドレス内文字列の判定を行う
	my $wfile;	# $wfileを局所化します。他で使っていなければやらなくてもOK
	if($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/i){
		$wfile = $file2;
		}else{
		$wfile = $file;
		}

 こんな感じになるかと思います。

 また、[例えば]ezwebが~@*.ezweb.ne.jp というメールアドレスも使い始めたら、「\@ezweb\.ne\.jp」の後にでも「|\.ezweb\.ne\.jp」を書き加えます。「\.ezweb\.ne\.jp」は、()外の$と合わさり「末尾が.ezweb.ne.jpで終わる」文字列にマッチします。

 ウィルコムも仲間に加える場合は、

($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp|\.pdx\.ne\.jp|\@pdx\.ne\.jp)$/i)

です。

id:icta

こんにちは、kiryuuさん

詳細に回答していただき本当にありがとうございました。

私はCGIとPerlの違いもよくわかっていない程の知識しか持ち合わせていないので丁寧に回答していただいて本当に助かりました。

時間がかかりましたが、ようやく教えていただいた内容をCGIに組み込むことに成功しました。

最後にもう1点だけ教えていただけませんでしょうか?

この実際に運用しているフォームの中には誕生月($birthmonth)と誕生日($birthday)を聞く欄があります。

誕生月と誕生日を教えたくない人もいますのでその場合はハイフン(-)を選択してもらいます。

ハイフンを選択した場合はCSVファイルに何も書き込まないようにために以下のように考えたのですが書き込まれてしまいます。

何か根本的なところを間違っているのでしょうか?

別の質問になるため本来は新たな質問を起こさなければならないは思うのですが、質問は800文字の文字制限があって質問の内容が正確に伝わりません。

最後にこの1点だけ教えていただければ幸いです。

if($birthmonth ne '-' || $birthday ne '-'){

open(FILE, "<$file") or &err2('エラーです。');

@lines= <FILE>;

close(FILE);

for($x=0;$x <= $#lines;$x++){

my @HashTmp = split("\,",$lines[$x]);

$Hname{$HashTmp[1]} = $HashTmp[1];

}

unless(exists($Hname{$mail})){

unshift(@lines,"$name,$mail,$birthmonth,$birthday\n");

open(OUT, "+<$file") or &err2();

print OUT @lines;

close(OUT);

}

2008/02/17 14:57:24

その他の回答1件)

id:tezcello No.1

回答回数460ベストアンサー獲得回数69

ポイント35pt

最近 perl を書いていないのでチョッと不安がありますが、こんな感じでしょうか。

1)

# 書きこむ

の行の前で、$newsletter の値が"希望する"または"登録済み"でない場合は、このルーチンから抜ければOKでは?

if ($newsletter eq '希望する' || $newsletter eq '登録済み') exit; # または return;


2)

こちらも

# 書きこむ

の行の前でキーワードを含めば、携帯用に書き換えるとよいと思います。

$file = './mail.csv';

if ($mail =~ /(docomo|ezweb|softbank|vodafone)/) $file = $file2; # 前以て$file2を定義せずにここで直接書いても可

id:icta

回答ありがとうございます。

1)はこういうことでしょうか?

エラーと表示されるので、恐らく書き方が間違っているのだと思われます。

勉強不足でPerlがよくわからないため、間違いがありましたらご指摘いただければ幸いです。

if ($newsletter eq '希望する' || $newsletter eq '登録済み'){

#書きこむ

open(FILE, "<$file") or &err2('エラーです。');

@lines= <FILE>;

close(FILE);

for($x=0;$x <= $#lines;$x++){

my @HashTmp = split("\,",$lines[$x]);

$Hname{$HashTmp[1]} = $HashTmp[1];

}

unless(exists($Hname{$mail})){

unshift(@lines,"$name,$mail,$birthmonth,$birthday\,\n");

open(OUT, "+<$file") or &err2();

print OUT @lines;

close(OUT);

}

exit;

}

2008/02/16 18:33:34
id:kiryuu No.2

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

ポイント400pt

まず条件を整理します。

1)$newsletterが「希望する」又は「登録する」の場合は書き込み処理を行う。

2)$mailにdocomo,ezweb,softbank,vodafoneのいずれかが含まれれば$file2に書き込み、当てはまらなければ$fileに書き込む。

1の条件式は、($newsletter eq '希望する' || $newsletter eq '登録する') となります。

2で真を返す正規表現は、($mail =~ /(docomo|ezweb|softbank|vodafone)/) となります。

他の方は$fileを上書きする方法を採られていますが、私は専用の変数(この場合$wfile)を使用する方が好きです。(単に好みの問題です)

$csv = "1";
$file = "./mail.csv";
$file2 = "./mail_keitai.csv";
$lock = "./lock/mail.dat";
$mail = $FORM{'mail'};
$name = $FORM{'name'};
$newsletter = $FORM{'newsletter'};
$mail2 = $FORM{'mail2'};

#書きこむ
#newsletterの値が'希望する'か'登録する'ならば実行する。
#それ以外なら何もしない

if($newsletter eq '希望する' || $newsletter eq '登録する'){

	#メールアドレス内文字列の判定を行う
	my $wfile;	# $wfileを局所化します。他で使っていなければやらなくてもOK
	if($mail =~ /(docomo|ezweb|softbank|vodafone)/){
		$wfile = $file2;
		}else{
		$wfile = $file;
		}

	open(FILE, "<$wfile") or &err2('エラーです。');
	@lines= <FILE>;
	close(FILE);

	for($x=0;$x <= $#lines;$x++){
		my @HashTmp = split("\,",$lines[$x]);
		$Hname{$HashTmp[2]} = $HashTmp[1]; 
		}

	unless(exists($Hname{$mail})){
		unshift(@lines,"$name,$mail\n");
		open(OUT, "+<$wfile") or &err2();
		print OUT @lines;
		close(OUT);
		}

	}

 ただ、質問文を読んでみると携帯電話用のアドレスとそれ以外のアドレスを別々のファイルで管理したいかの様に思えます。

 ($mail =~ /(docomo|ezweb|softbank|vodafone)/) ですと、例えば「docomonsense@hatena.ne.jp」(DoCommonSenseのミススペル)も「携帯電話アドレス」と判定されます。

 Docomoのメールアドレスは、~@docomo.ne.jp

 ezwebのメールアドレスは、~@ezweb.ne.jp

 SoftBankのメールアドレスは、~@softbank.ne.jp

 Vodafoneのメールアドレスは、~@*.vodafone.ne.jp

ですので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)/)

が適切かと。

 更に、これらのドメインは末尾にくるはずなので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/)

この場合の//内の「$」は、文字列の末尾を表します。

 ついでに、ドメインネームについては大文字小文字を区別しませんので、

 ($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/i)

//の外の「i」は、大文字小文字を区別しないパターンマッチを行う修飾子です。


 これをメールアドレス内文字列の判定に適用すると、

	#メールアドレス内文字列の判定を行う
	my $wfile;	# $wfileを局所化します。他で使っていなければやらなくてもOK
	if($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp)$/i){
		$wfile = $file2;
		}else{
		$wfile = $file;
		}

 こんな感じになるかと思います。

 また、[例えば]ezwebが~@*.ezweb.ne.jp というメールアドレスも使い始めたら、「\@ezweb\.ne\.jp」の後にでも「|\.ezweb\.ne\.jp」を書き加えます。「\.ezweb\.ne\.jp」は、()外の$と合わさり「末尾が.ezweb.ne.jpで終わる」文字列にマッチします。

 ウィルコムも仲間に加える場合は、

($mail =~ /(\@docomo\.ne\.jp|\@ezweb\.ne\.jp|\@softbank\.ne\.jp|\.vodafone\.ne\.jp|\.pdx\.ne\.jp|\@pdx\.ne\.jp)$/i)

です。

id:icta

こんにちは、kiryuuさん

詳細に回答していただき本当にありがとうございました。

私はCGIとPerlの違いもよくわかっていない程の知識しか持ち合わせていないので丁寧に回答していただいて本当に助かりました。

時間がかかりましたが、ようやく教えていただいた内容をCGIに組み込むことに成功しました。

最後にもう1点だけ教えていただけませんでしょうか?

この実際に運用しているフォームの中には誕生月($birthmonth)と誕生日($birthday)を聞く欄があります。

誕生月と誕生日を教えたくない人もいますのでその場合はハイフン(-)を選択してもらいます。

ハイフンを選択した場合はCSVファイルに何も書き込まないようにために以下のように考えたのですが書き込まれてしまいます。

何か根本的なところを間違っているのでしょうか?

別の質問になるため本来は新たな質問を起こさなければならないは思うのですが、質問は800文字の文字制限があって質問の内容が正確に伝わりません。

最後にこの1点だけ教えていただければ幸いです。

if($birthmonth ne '-' || $birthday ne '-'){

open(FILE, "<$file") or &err2('エラーです。');

@lines= <FILE>;

close(FILE);

for($x=0;$x <= $#lines;$x++){

my @HashTmp = split("\,",$lines[$x]);

$Hname{$HashTmp[1]} = $HashTmp[1];

}

unless(exists($Hname{$mail})){

unshift(@lines,"$name,$mail,$birthmonth,$birthday\n");

open(OUT, "+<$file") or &err2();

print OUT @lines;

close(OUT);

}

2008/02/17 14:57:24
  • id:tezcello
    間違ってますねぇ...

    条件部分を、
    (!($newsletter eq '希望する' || $newsletter eq '登録する')) にするか、
    ($newsletter ne '希望する' && $newsletter ne '登録する') にするかで迷ってて...
    ブール代数の基本なのでお分かりになるとは思いましたが、質問文に近い形の方が理解しやすいかなぁと。
    その結果が間違っていては「何だかなぁ〜」ですが。

    どんなエラーが表示されるのでしょうか? >ictaさん
    (サーバにもよりますが)どの行でとか、どんなエラーとか出てませんか?
    もしも「エラーです。」だけ出ているのでしたら、ファイルのオープンに失敗したときにスクリプト中で出力しているヤツでは?(カレントのディレクトリにはperlに作成のパーミッションがないとか...)

    仰せの通りです >kiryuu さん
    知り合いに、○○○.vodafone.ne.jp@ezweb.ne.jp なんてアドレスをつけているヤツが居たのを思い出しました。
    なので、@以降である事は明示すべきでしょうね。
    vodafone も、@[dhtcrknsq]¥.vodafone¥.ne¥.jp で指定可能ですね。
    KDDI, Wilcom がどのようなアドレスなのかは知りませんが、@¥w*?¥.?ezweb¥.ne¥.jp のように出来るかも知れません。(ごめんなさい。うまくマッチするかは未確認です)
    で、全部に@があるのなら()の前に出してしまった方が見やすいですね。
    (たしか、@はメタ文字ではなかったと思うので、エスケープは不要では?)

    2)の方は、いつもなら
    $file = './mail';
    if ($mail =~ ........) $file .= '_keitai';
    $file .= '.csv';
    とするだろうなぁと思います。せっかく $file2 が用意されているので回答したようにしました。
    (.csv を_keitai.csv と置き換えるように書いたのもあったっけ)
    自分でファイル名を決めるのなら、文字の連結が2回で済むようにする為、'mail.csv' , 'kmail.csv' か、'csv_mail', 'csv_mail_k' (あまりWinからダブルクリックで開く事を重視しないので、拡張子にcsvが無くても気にしない為)にすると思います。
    まぁ、やり方は人それぞれで、色々あるって事ですね。
  • id:kiryuu
    >tezcello さん
     実際に動かしてみました。@はエスケープ不要ですね。ご指摘ありがとうございます。
     vodafoneのサブドメインについては、資料がちょっと見つからなかったのですが、今しがたヤフオクの「モバイル版「Yahoo!かんたん決済」のURLをケータイに送信する」のフォームを見たところ、[dhtcrknsq]であっているようです。(Vodafoneを引き継いだSoftBankの記述をある程度信頼するとして)

    /@(docomo\.ne\.jp|ezweb\.ne\.jp|softbank\.ne\.jp|[dhtcrknsq]\.vodafone\.ne\.jp|\w*?\.?pdx\.ne\.jp)$/i

    でテストしてみましたが、どうやら正常に動いているようです。(perl 5.8.6)

    エラーと表示される件:
     tezcelloさんご指摘の通り、「エラーです。」表示でしたら、ファイル操作周辺でのエラーかと思います。
     open(FILE, "<$file")  で失敗したときに出す設定ですので、$file(mail.csv若しくはmail_keitai.csv)が
      ・読み込むべきファイルが存在しない
      ・ファイルのパーミッションが適切でない
     のいずれかが原因だと推測します。
     ファイルがない場合は、空ファイルを作ってFTPでアップするか(シェル上でtouchしてもいいかと)、スクリプト内で作成するのがよろしいかと。
     スクリプト内で処理するとなるとこんな感じ?
    >||
    if(-e $wfile){
    open(FILE, "<$wfile") or &err2('エラーです。');
    @lines= <FILE>;
    close(FILE);
    }else{
    open(FILE,">$wfile") or &err2("$fileの生成に失敗");
    close(FILE);
    chmod 0606,$wfile;
    @lines = ();
    }
    ||<
    ($wfileは私の回答の場合)
    カレントディレクトリのパーミッションを707等にするのをお忘れなく。
    707が不安なのであれば、やはり予めファイルを作っておく必要があります。

     パーミッションが合っていない場合は、$fileのファイルのパーミッションを設定しましょう。(606。suExec下であれば600)



    #ifのブロック内が長くなるのはあまり好きじゃないので、私の場合はサブルーチンに分けたりしちゃいます。
  • id:tezcello
    > ($birthmonth ne '-' || $birthday ne '-')
    これを日本語にすると、
    $birthmonth が '-' でない、または、$birthday が '-' でない の場合となります。
    つまり、$birthmonth と $birthday のどちらか一方でも '-' ではない場合は書き込み処理をしてしまいます。
    お望みの処理は、どちらか一方でも '-' が選択された場合は書き込まない というのだと思うのですが、それならば
    ($birthmonth ne '-' && $birthday ne '-')
    という事でしょうね。
  • id:kiryuu
    tezcelloさん>($birthmonth ne '-' || $birthday ne '-')
    >これを日本語にすると、
    >$birthmonth が '-' でない、または、$birthday が '-' でない の場合となります。

    ですね。
    $birthmonth か $birthday どちらか(又は両方)が“-”でなければという条件なら
    ($birthmonth ne '-' || $birthday ne '-')
    ですが、
    両方“-“でない場合の式は、
    ($birthmonth ne '-' && $birthday ne '-')
    であるというのは同見解です。

    ついでに、$newsletterの条件と合わせるなら
    (($newsletter eq '希望する' || $newsletter eq '登録する') && ($birthmonth ne '-' && $birthday ne '-'))
    でしょうか。
  • id:icta
    動作確認に時間がかかってお礼を申し上げるのが遅くなってすみませんでした。
    期待通りの仕様で大変うれしいです。
    これまで何年も手作業で行っていた作業からようやく解放されます。
    完璧に動作するのを見たらこれまで何と時間を無駄にしてきたことかと考えさせられました。
    CGIはまだよくわかっていないのでこれを契機に勉強してみたいと思います。
    本当にありがとうございました。

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

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

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

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