指数正则表达式问题

时间:2011-06-17 00:15:57

标签: regex perl parsing email

有人可以帮我改写这个正则表达式是非指数的吗?

我正在使用perl来解析电子邮件数据。我想从数据中提取电子邮件地址。这是我一直在使用的正则表达式的缩短版本:

my $email_address = qr/(?:[^\s@<>,":;\[\]\(\)\\]+?|"[^\"]+?")@/i

为简单起见,我删除了正则表达式的后续域部分。 (这不会造成任何问题。)

这将找到符合RFC的电子邮件地址,该地址包含非电子邮件元字符或“引用”字符串,后跟@。使用OR'|'具有两种不同多字符模式的正则表达式的一部分会产生指数问题。

问题是,当我在一行长达数千个字符的数据线上释放它时。

$ wc line7.txt 
1    221 497819 line7.txt

(对不起,但我目前无法提供输入数据,我可能稍后会模拟一些。)

就像重写(a * b *)*到(a | b)*一样,我需要重写这个正则表达式。

将它拆分成两个独立的正则表达式会在代码更改中创建更多工作,然后我愿意在这一点上执行。虽然它可以解决我的问题。

最终目标计算机位于Hadoop集群上。所以我想避免使用没有Hadoop版本的perl的CPAN模块。 (我必须检查是否可以使用Email :: Find。)这是我在工作中遇到的问题。

5 个答案:

答案 0 :(得分:7)

您是否考虑过CPAN模块Email::ValidEmail::Find

除非这是为了您自己的乐趣或教育,否则您几乎肯定不应该尝试编写与正则表达式匹配的自己的电子邮件地址。如果您想知道这样的事情究竟是什么样子,请参阅Jeffrey Friedl掌握正则表达式。 (提示:长度为6,598字节。)

答案 1 :(得分:0)

如果你不小心,非贪婪的比赛是昂贵的,因为我理解它。它可能会做很多很多的回溯。 http://blog.stevenlevithan.com/archives/greedy-lazy-performance

我经常使用的一个技巧是,一旦我发现它无法保存任何数据,就会破坏性地将数据位拉出。另一个技巧是进行非回溯匹配(\ @ {1} +之类),如果有什么东西可能会向你发出信号,确实有一个你需要在那里解析的电子邮件地址。

在您的具体示例中,您是否可以限制电子邮件地址中可以包含的字符数?而不是@左侧的+,使用{1,80}

答案 2 :(得分:0)

只需将+?更改为+即可; ?表示希望尽可能少地匹配,这根本不是你想要的。

要么我看错了,要么你的问题出现在你没有向我们展示的正则表达式中。或者你所展示的内容与你实际尝试的内容之间存在一些差异。在任何情况下,您都可以尝试将+?更改为++或将整个(?:...)@括在(?> ... )中。

在您的实际正则表达式+之前是否有@?如果是这样,只需将(?:更改为(?>并将 <{em> +改为++,这将是一个非常好的主意。

答案 3 :(得分:0)

如果很多行包含电子邮件地址,那么在应用RE之前如何进行快速预测试:

if ( my $ix = index( $line, '@' ) > 0 )
{   #test E-mail address here
    . . .
    #and another wild idea you could try to cut down lengths of strings actually parsed:
    my $maxLength = 100;     #maximum supported E-mail address length (up to the @)
    if ( substr( $line, MAX( $ix - $maxLength, 0), $maxLength ) =~ /YourRE/ )
}

(是的,&gt;以@开头的任何行都不能是电子邮件地址)

答案 4 :(得分:0)

qr/(?:(?>[^\s@<>,":;\[\]\(\)\\])+|"[^\"]{0,62}")@/i

(?>expression)部分可防止回溯。它应该是安全的,因为非引用部分和引用部分之间不能重叠。

我删除了延迟重复+?,因为替换部分已分别查找@"。短语可能是回溯的重要来源,所以我看了维基百科文章,该文章指出本地部分(在@之前)只能是64个字符(减去两个引号会产生{0,62}(如果""@是无效的,然后将其更改为{1,62} ....我不打算将其作为一个功能完备的电子邮件解析器。这是你的工作。我只是为灾难性的回溯提供帮助。)祝您好运!