【问题标题】:Regex hangs for a pattern match正则表达式挂起模式匹配
【发布时间】:2012-10-13 09:30:22
【问题描述】:

我有输入文件here(请解压)和下面的代码。就在 print "pat=\n"; 之后它挂在模式匹配中,而我在 Windows 任务管理器 Perl(v5.010000) 中看到占用 25% 的 CPU 时间:

use strict;
open FP, "<default.php" or die "can't read";

$/ = undef;    

my $content = <FP>;

while ( $content =~ /(['"])([^\s\x00-\x1f]{300,})\1/gs ) {
#looks like we've found base encoded string etc
    my $subpat = $2;
    print "pat=<$subpat>\n";
    if ( $subpat =~ m#(?:(....).{5,30}(?=\1)){50}#s ) {
      print "hello world";
    } else {
      die("");
    }
    exit;
}

编辑:理想情况下,我想在连续组(最大 50 个)中找出任何统一的字节重复模式(长度:4),其中每个组的大小可以是最大 4+30 字节和最小 4+5 字节。

例如(在每组中重复 '=>' 大小为 1 到 30 个字节):

'cs'=>'捷克语','da'=>'丹麦语','nl'=>'荷兰语','fi'=>'芬兰语','fr'=>'法语','de '=>'德语','el'=>'希腊语',

行:打印“pat=\n”;打印以下内容并挂起:

pat='克罗地亚语','cs'=>'捷克语','da'=>'丹麦语','nl'=>'荷兰语','fi'=>'芬兰语','fr'=>'法语','de'=>'德语','el'=>'希腊语','hi'=>'印地语','it'=>'意大利语','ja' =>'日文','ko'=>'韩文','no'=>'挪威文','pl'=>'波兰文','pt'=>'葡萄牙文','ro'=>'罗马尼亚文' ,'ru'=>'俄语','es'=>'西班牙语','sv'=>'瑞典语','ca'=>'加泰罗尼亚语','tl'=>'菲律宾语','iw'= >'希伯来语','id'=>'印度尼西亚语','lv'=>'拉脱维亚语','lt'=>'立陶宛语','sr'=>'塞尔维亚语','sk'=>'斯洛伐克语', 'sl'=>'斯洛文尼亚语','uk'=>'乌克兰语','vi'=>'越南语','sq'=>'阿尔巴尼亚语','et'=>'爱沙尼亚语','gl'=> '加利西亚语','hu'=>'匈牙利语','mt'=>'马耳他语','th'=>'泰语','tr'=>'土耳其语','fa'=>'波斯语',' af'=>'南非荷兰语','ms'=>'马来语','sw'=>'斯瓦希里语','ga'=>'爱尔兰语','cy'=>'威尔士语','be'=>'白俄罗斯语','is'=>'冰岛语','mk'=>'马其顿语','yi'=>'意第绪语','hy'=>'亚美尼亚语','az'=>'阿塞拜疆语','eu '=>'巴斯克语','ka'=>'格鲁吉亚语','ht'=>>

【问题讨论】:

  • 您能用文字解释一下您希望正则表达式匹配什么等吗?
  • @martin Clayton 我已经在 EDIT 中更新了

标签: regex perl


【解决方案1】:

Perl 正则表达式并不是特别聪明。您的模式是通过回溯实现的——基本上,在每个决策点(例如,未锚定的开始,或{5,30}),正则表达式将其状态保存在堆栈中,并以尽可能小的匹配继续前进。如果它卡住了,它会从堆栈中弹出一个级别。

通常这工作得很好,因为输入的大小是有限的,那里有文字或字符类匹配以尽早切断失败的匹配,并且嵌套变量重复指令的使用受到限制。此外,当不存在回溯时,正则表达式可以转换(理论上,虽然 perl 不这样做)为确定性有限自动机,可以有效地执行。

在这里,你有很多通配符匹配,一个大输入字符串,加上一个有效的 50 次迭代循环,中间有一个 25 层分支,围绕它的循环试图找到开始和结束的比赛。在最坏的情况下,您的正则表达式可能不得不回溯 25^50 次,而这甚至没有考虑缺少开始或结束锚点。

因此,您需要找到一种更聪明的方法来使该算法发挥作用。尝试只生成字符串的每个 4 个字符的子字符串,并计算出现次数。对于每个出现多次的子字符串,检查找到的匹配项的偏移量,看看你是否有你的模式。

【讨论】:

  • 在将长度从 50 减少到 30 时,它能够通过 ( m#(?:(....).{5,30}(?=\1)){30}# s)
  • @AgA,它仍在回溯大量,只是 30 意味着您有更多潜在的匹配方式(即,这是一个约束较少的问题),因此它需要更少的回溯来找到匹配解决方案。
【解决方案2】:

直观地查看回溯及其成本的一个好方法是使用Regexp::Debugger 为您的正则表达式建模

Jeffrey Friedl 的“掌握正则表达式”是一个很好的伴侣,其中详细讨论了回溯。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 2016-04-20
    • 2014-09-15
    相关资源
    最近更新 更多