regex 如何从任意文本中提取电子邮件标题和邮件ID?

h9vpoimq  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(96)

下面的测试程序说明了我在尝试区分MessageID和电子邮件地址时遇到的一个问题,特别是当我事先不知道我正在解析电子邮件标题时。

#!/opt/perl/bin/perl
  # use Regexp::Debugger;
  use warnings;
  no warnings qw(experimental::vlb);

  my $re = qr{
          (
              (?:
                  # one or more of these
                  [\=a-z0-9!\#$%&'*+/?^_`{|}~-]+
                  # zero or more of these
                  (?:\.[\=a-z0-9!\#$%&'*+/?^_`{|}~-]+)*
              )
              @
              (?:
                  (?!\d+\.\d+)
                  (?=.{4,255})
                  (?:
                      (?:[a-zA-Z0-9-]{1,63}(?<!-)\.)+
                      [a-zA-Z0-9-]{2,63}
                  )
              )
          )
  }xims;
  my $text = <<'EOF';

  Arbitrary text followed by a snippet of an email header:

  To: "T B" <[email protected]>, "Foobar" <[email protected]>
  Message-ID: <[email protected]>

  More text.

  EOF

  while ( $text =~ m/$re/g ) {
      print "$1\n";
  }

字符串
输出量:

[email protected]
[email protected]
[email protected]


我想要的输出是

[email protected]
[email protected]


我试着在(?<=To:\ )后面添加一个外观,但没有匹配。
更大的程序对输入文本应用了几百个正则表达式。每个正则表达式都是一个特定的类型,例如foo => qr/[Ff]oo/,如果匹配,则该文本会被一个标记“ Package ”,以标识它匹配的正则表达式。例如foo。

jchrr9hc

jchrr9hc1#

随着问题的澄清(以及不只是请求正则表达式的变化),这里有一个关于它的看法。
首先提取所有的标题,每个标题都有下一个标题的文本(因为我们不知道如果它嵌入在文本中,标题在哪里停止)。然后我们可以从每个这样的项目中提取地址,并且只从我们想要的标题中提取。必须首先获得 * 所有 * 标题,否则不需要的标题将被我们匹配的标题所吸收。

use warnings;
use strict;
use feature 'say';

my $text = do { local $/; <DATA> };  # slurp all text into a scalar    
#say $text; say '-'x60;

# These better be all headers with email addresses in the text
my $hdr_re = qr/To|From|Message-ID/;
my @headers_plus = $text =~ /( (?:$hdr_re): .*? )(?=(?:$hdr_re|$))/sxg;

#say "\nHeaders with the following text (until next header):\n";
#say "$_\n---\n" for @headers_plus;

foreach my $hdr_plus (@headers_plus) {
    next if not $hdr_plus =~ /^\s*(To|From)/;
    my $header_type = $1;

    my @addresses = $hdr_plus =~ /<([^>]+)>/g;

    say "Addresses for |$header_type| header:";
    say for @addresses;
    say '';
}

__DATA__
Arbitrary text followed by a snippet of an email header:

To: "T B" <[email protected]>, "Foobar" <[email protected]>
Message-ID: <[email protected]>
From: "X Y" <[email protected]>,
"Other" <head[email protected]>

To: "Yo" <[email protected]>

More text.

字符串
请注意,我在问题的文本中添加了一些标题,一个多行。
这是相当基本的,我相信有一些情况下没有得到正确的捕捉;解析电子邮件标题是棘手的。但希望它适用于问题中所示的简化情况。
也可以过滤掉不需要的标题了

my @headers_plus = 
    grep { /^\s*To|From/ }
    $text =~ /( (?:$hdr_re): .*? )(?=(?:$hdr_re|$))/sxg;


然后你可以扔在一个map和获取地址的权利,以及那里,但我不认为有理由塞它这样。
文本中的正则表达式允许标题在一行中的任何位置开始。但是如果它们总是在一行的开头开始,那么这可能是一个很好的限制。那么我们将有

my @headers_plus = $text =~ /^\s*( (?:$hdr_re): .*? )(?=(?:$hdr_re|\Z))/msxg;


现在我们需要'multiline'修饰符(/m),这样^就可以匹配文本中的新行。然后整个字符串的结尾就是\Z(因为$现在匹配文本中每一行的结尾)。

fv2wmkja

fv2wmkja2#

  • Message-ID* 字段应仅包含一个地址。
  • RFC 2822 - Internet消息格式- 3.6.4.标识字段 *
  • .“Message-ID:“字段包含一个唯一的消息标识符。.*

尝试以下 * 捕获模式 *。

(?<!^Message-ID:\s)<(.+?)>

字符串
或者,匹配模式。

(?<!^Message-ID:\s<)(?<=<).+?(?=>)

相关问题