RegExp只在perl中有无限循环,为什么?

时间:2012-05-10 16:16:59

标签: regex perl loops path regex-group

我有一个正则表达式来测试CSV单元格是否包含正确的文件路径:

编辑 CSV列出了脚本运行时尚不存在的文件路径(我不能使用-e),文件路径可以包含*或%variable%或{$ variable}。

my $FILENAME_REGEXP = '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s\.\*-]+|\{\$\w+}|%\w+%)[\\\/]{0,2})*\1$';

由于CSV单元格有时包含双引号的包装,有时文件名本身需要用双引号括起来,所以我进行了这种分组(|“|”“”)... \ 1

然后使用此功能:

sub ValidateUNCPath{
    my $input = shift;
    if ($input !~ /$FILENAME_REGEXP/){
        return;
    } 
    else{
        return "This is a Valid File Path.";
    }

}

我正在尝试测试这个短语是否与我的正则表达式相匹配(它应该不匹配):

"""c:\my\dir\lord"

但是亲爱的Perl在以下情况下进入无限循环:

ValidateUNCPath('"""c:\my\dir\lord"');

编辑实际上它会循环播放:

ValidateUNCPath('"""\aaaaaaaaa\bbbbbbb\ccccccc\Netwxn00.map"');

我确保在http://regexpal.com中我的正则表达式正确地捕获了那些包含双引号的非对称“”“...”,但Perl有了自己的想法:(

我甚至尝试了

中的/ g和/ o标志
/$FILENAME_REGEXP/go

但它仍然悬而未决。我错过了什么?

4 个答案:

答案 0 :(得分:3)

首先,你发布的任何内容都不会导致无限循环,所以如果你得到一个,那么它不会来自代码的这一部分。

当我尝试你的子程序时,对于远离看起来像路径的各种字符串,它返回true,例如:

.....
This is a Valid File Path.
.*.*
This is a Valid File Path.
-
This is a Valid File Path.

这是因为你的正则表达式相当松散。

^(|"|""")                  # can match the empty string
(?:[a-zA-Z]:[\\\/])?       # same, matches 0-1 times
[\\\/]{0,2}                # same, matches 0-2 times
(?:(?:[\w\s\.\*-]+|\{\$\w+}|%\w+%)[\\\/]?)+\1$  # only this is not optional

由于只有最后一部分实际上必须匹配任何东西,所以你允许所有类型的字符串,主要是在第一个字符类中:[\w\s\.\*-]

在我个人看来,当你开始依赖看起来像你的正则表达式时,你做错了什么。除非你熟练使用正则表达式,并且希望没有人不会被迫修复它。

为什么不删除引号?此外,如果系统中存在此路径,则可以更轻松地检查其是否有效:-e $path

答案 1 :(得分:1)

更新

编辑:从反复试验中,以下分组子表达式[\w\s.*-]+导致回溯问题

    (?:
        (?:
             [\w\s.*-]+
          |  \{\$\w+\}
          |  %\w+%
        )
        [\\\/]?
    )+

修复#1, 展开的方法

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?:
        [\w\s.*-]
      |  \{\$\w+\}
      |  %\w+%
    )+
    (?:
        [\\\/]
        (?:
            [\w\s.*-]
          |  \{\$\w+\}
          |  %\w+%
        )+
    )*
    [\\\/]?
    \1
 $
'

修复#2,独立子表达式

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?>
       (?:
           (?:
                [\w\s.*-]+
             |  \{\$\w+\}
             |  %\w+%
           )
           [\\\/]?
       )+
    )
    \1
 $
'

修复#3,删除+量词(或添加+?)

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?:
        (?:
             [\w\s.*-] 
          |  \{\$\w+\}
          |  %\w+%
        )
        [\\\/]?
    )+
    \1
 $
'

答案 2 :(得分:1)

如果正则表达式引擎是天真的,

('y') x 20 =~ /^.*.*.*.*.*x/
由于必须尝试

需要很长时间才能失败

20 * 20 * 20 * 20 * 20 = 3,200,000 possible matches.

您的模式具有类似的结构,这意味着它有许多组件可以匹配您输入的各种子字符串。

现在,Perl的正则表达式引擎高度优化,远非天真。在上面的模式中,它将首先查找x,然后非常快地退出。不幸的是,它没有或无法同样优化您的模式。

你的模式完全混乱。我甚至不打算猜测它匹配的假设。一旦切换到正确的模式,您会发现此问题将自行解决。

答案 3 :(得分:0)

感谢sln这是我的固定正则表达式:

my $FILENAME_REGEXP = '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s.-]++|\{\$\w+\}|%\w+%)[\\\/]{0,2})*\*?[\w.-]*\1$';

(我也不允许在目录中使用* char,并且只允许使用单个* in(最后)文件名)