努力してみた日記

«前の日記(2012-04-13) 最新 次の日記(2012-04-24)» 編集
あまりためにはならない話しか書かないと思うよ。

努力したWiki

2012-04-17 油断できないのぅ

[雑記] 解除

携帯通知解除っと

[mail] メールヘッダからメールアドレスを取り出す2

前回の続き。

まー、経験のある方々なら前回の正規表現の罠もすっかりお見通しですよねー。

my $Cc = 'abc@hoge.jp, "def@hoge.jp" <ghi@hoge.jp>';
my @addrs = $Cc =~ /((?:[a-zA-Z0-9\.!#\\$%&'*+-\/=\?\^_\`\{\|\}\~]+|"[a-zA-Z0-9\.!#\\$%&'*+-\/=\?\^_\`\{\|\}\~ \(\)<>\[\]:;@,\"\\]+")@[a-zA-Z0-9\.-]+)/g;
 
my $i=1;
foreach my $addr ( @addrs )
{
  print "$i: $addr\n";
  $i++;
}
EOF

結果はというと。

1: abc@hoge.jp
2: def@hoge.jp
3: ghi@hoge.jp

出て欲しいのは abc@hoge.jp と ghi@hoge.jp の二つなんですよ。

名前部分にメールアドレスらしきものがあると駄目になる例ですね。

これを避けるには、ダブルクォートではさまれた部分を無視するようにしなくちゃいけないんだけど、

 "this is test"@hoge.jp

みたいなメールアドレスもありうるので(実質使われていないとは思うんですけどね)判断が難しいんですよ。

名前部分の囲みなのかメールのローカル部の囲みなのか、これじゃあ判断できないですー、ってことに。

じゃあ名前とメールアドレスをパースしちゃえばいいじゃない!

メールアドレスだけをパースしようとするからいけないんだ。名前とメールアドレスをパースしてあとでメールアドレスだけ集めちゃえばいいじゃない!ってことで、無理に正規表現をこねくり回さないで素直に書いてみたのが次のコード。

my $Cc = 'abc@hoge.jp, "def@hoge.jp" <ghi@hoge.jp>, "are you \"ready\" ?" jkl@hoge.jp, ThisIsTest mno@hoge.jp ( other email pqr@hoge.jp ), "this is test"@hoge.jp';
my @addrs = $Cc =~/"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+"(?!@)|\((?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+\)|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~]+@[a-zA-Z0-9\.-]+|"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~ \(\)<>\[\]:;,@])+"@[a-zA-Z0-9\.-]+/g;
 
$i=1;
print "****\n$Cc\n****\n";
foreach my $ml ( @addrs )
{
  $a = ($ml =~ /^["\(].+["\)]$/) ? " " : "*";
  printf("%03i: %1s [%s]\n", $i, $a, $ml);
  $i++;
}

ええ、こそーっと一部正規表現をなおしてます。途中で気が付いちゃったもんで(^^; 実行するとこんな感じ。

****
abc@hoge.jp, "def@hoge.jp" <ghi@hoge.jp>, "are you \"ready\" ?" jkl@hoge.jp, ThisIsTest mno@hoge.jp ( other email pqr@hoge.jp ), "this is test"@hoge.jp
****
001: * [abc@hoge.jp]
002:   ["def@hoge.jp"]
003: * [ghi@hoge.jp]
004:   ["are you \"ready\" ?"]
005: * [jkl@hoge.jp]
006: * [mno@hoge.jp]
007:   [( other email pqr@hoge.jp )]
008: * ["this is test"@hoge.jp]

アスタリスクの付いた、001,003,005,006,008がお目当てのメールアドレスになります。つまり、切り出した要素の両脇がダブルクォートか括弧で囲まれてなければメールアドレスと判断すればよろし。

007は、メールアドレスのあとに書けるコメントです。こちらにもメールアドレスが書かれてしまう可能性があるので組み込んでおきました。

正規表現が長いのでちょっと説明

先の正規表現では4つの文字列にマッチングします。

  1. "(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+"(?!@)
    ダブルクォートで囲まれ最後が『〜"@』で終わらない文字列にマッチングします。『〜"@』で終わるとメールアドレスのローカル部にもマッチングしてしまうためです。
  2. \((?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+\)
    ダブルクォートの替わりに括弧"()"で囲まれた文字列のマッチングします。
  3. [a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~]+@[a-zA-Z0-9\.-]+
    ローカル部がダブルクォートで囲まれていないメールアドレスにマッチングします。
  4. "(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~ \(\)<>\[\]:;,@])+"@[a-zA-Z0-9\.-]+
    ローカル部がダブルクォートで囲まれているメールアドレスにマッチングします。

一行の正規表現でローカル部のダブルクォート囲みか名前のダブルクォート囲みか判断させるのは困難でした。なのでこの方法に落ち着くしかありませんでした。

もしうまい手があれば教えてください。いや本当に。

[mail] メールヘッダからメールアドレスを取り出す3

これは昨日会社でやっと気が付いたもの。お間抜けですわ(^^;

my $Cc = 'abc@hoge.jp,"def@hoge.jp",jkl@hoge.jp,ThisIsTest,mno@hoge.jp,"this is test"@hoge.jp';
my @addrs = $Cc =~/"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+"(?!@)|\((?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+\)|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~]+@[a-zA-Z0-9\.-]+|"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~ \(\)<>\[\]:;,@])+"@[a-zA-Z0-9\.-]+/g;
 
$i=1;
print "****\n$Cc\n****\n";
foreach my $ml ( @addrs )
{
  $a = ($ml =~ /^["\(].+["\)]$/) ? " " : "*";
  printf("%03i: %1s [%s]\n", $i, $a, $ml);
  $i++;
}

実行すると

****
abc@hoge.jp,"def@hoge.jp",jkl@hoge.jp,ThisIsTest,mno@hoge.jp,"this is test"@hoge.jp
****
001: * [abc@hoge.jp]
002:   ["def@hoge.jp"]
003: * [,jkl@hoge.jp]
004: * [,ThisIsTest,mno@hoge.jp]
005: * ["this is test"@hoge.jp]

何かおかしいですよね。カンマがメールアドレス構成文字として許されているように見えます。

...原因はここ。プラス記号とマイナス記号に注目。

[a-zA-Z0-9\.!#$%&'*+-\/=\?\^_\`\{\|\}\~]

a-zで英小文字a〜zを示します。じゃあ+-/は?

+-/の範囲

確かにカンマ入ってるよね(>_<)

早速この部分を修正して再度実行してみると

my $Cc = 'abc@hoge.jp,"def@hoge.jp",jkl@hoge.jp,ThisIsTest,mno@hoge.jp,"this is test"@hoge.jp';
my @addrs = $Cc =~/"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+\-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+"(?!@)|\((?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+\-\/=\?\^_\`\{\|\}~ \(\)<>\[\]:;,@])+\)|[a-zA-Z0-9\.!#$%&'*+\-\/=\?\^_\`\{\|\}\~]+@[a-zA-Z0-9\.-]+|"(?:\\"|\\\\|[a-zA-Z0-9\.!#$%&'*+\-\/=\?\^_\`\{\|\}\~ \(\)<>\[\]:;,@])+"@[a-zA-Z0-9\.-]+/g;
 
$i=1;
print "****\n$Cc\n****\n";
foreach my $ml ( @addrs )
{
  $a = ($ml =~ /^["\(].+["\)]$/) ? " " : "*";
  printf("%03i: %1s [%s]\n", $i, $a, $ml);
  $i++;
}

結果はこうなりました。

****
abc@hoge.jp,"def@hoge.jp",jkl@hoge.jp,ThisIsTest,mno@hoge.jp,"this is test"@hoge.jp
****
001: * [abc@hoge.jp]
002:   ["def@hoge.jp"]
003: * [jkl@hoge.jp]
004: * [mno@hoge.jp]
005: * ["this is test"@hoge.jp]

もう大丈夫でしょう。


単なる覚書以下の内容です。一度内容を全部消しました。
«前の日記(2012-04-13) 最新 次の日記(2012-04-24)» 編集
2010|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|03|04|05|06|07|08|09|11|12|
2013|01|02|03|04|06|08|
2014|02|04|06|07|09|10|11|12|
2015|01|02|03|04|06|08|09|10|11|12|
2016|01|02|04|05|10|
2017|02|03|04|05|06|09|10|
2018|04|