0

我有一个正则表达式来测试 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

4 回答 4

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

于 2012-05-10T16:52:54.113 回答
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
 $
'
于 2012-05-10T17:08:42.357 回答
1

如果正则表达式引擎很幼稚,

('y') x 20 =~ /^.*.*.*.*.*x/

需要很长时间才能失败,因为它必须尝试

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

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

现在,Perl 的正则表达式引擎经过高度优化,远非天真。在上面的模式中,它会从寻找开始x,然后非常非常快地退出。不幸的是,它不会或不能同样优化您的模式。

你的模式是一团糟。我什至不会尝试猜测它应该匹配什么。您会发现,一旦切换到正确的模式,这个问题就会自行解决。

于 2012-05-10T18:34:48.243 回答
0

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

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

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

于 2012-05-11T00:21:10.327 回答