Python re.match需要更长的时间来匹配它

时间:2015-08-01 19:50:00

标签: python regex

我需要找到匹配模式的输入字符串,如下所示:

'fe{10,20}.clustera1.example.com'
'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com'

主机名中的主机名或{}块可以在输入字符串中重复多次。

我首先尝试使用re模块匹配,在某些情况下需要10-30秒。例如,如果在输入字符串的末尾添加了一个空格,如下所示:

'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com '

这需要很长时间才能完成。

import re
string = 'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com '
print re.match('^([a-z.-]+|{[\d]+(,[\d]+)*})+(,([a-z.-]+|{[\d]+(,[\d]+)*})+)*$', string).group(0)

即使是简化版本(不检查,块内{}的正确位置)也会表现相同。

print re.match('^([a-z.-]+|{[\d,]+})+(,([a-z.-]+|{[\d,]+})+)*$', string).group(0)

在Perl中使用Python正则表达式模块尝试了相同的正则表达式。两者都很好而且速度很快。

在这里,两者都没有匹配(预期),但效果非常快。

echo 'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com ' | \
perl -nle 'print $_ if /^([a-z.-]+|{[\d]+(,[\d]+)*})+(,([a-z.-]+|{[\d]+(,[\d]+)*})+)*$/'

import regex
string = 'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com '
print re.match('^([a-z.-]+|{[\d]+(,[\d]+)*})+(,([a-z.-]+|{[\d]+(,[\d]+)*})+)*$', string).group(0)

我使用的正则表达式模式有什么问题吗?是否可以使用re模块本身工作?

用于测试的Python版本是2.7.6和2.7.8

2 个答案:

答案 0 :(得分:3)

您的输入字符串示例有一个尾随空格,但您的正则表达式不允许尾随空格。所以,其中任何一个:

>>> text = 'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com'
>>> re.match('^([a-z.-]+|{[\d,]+})+(,([a-z.-]+|{[\d,]+})+)*$', text)
    <_sre.SRE_Match object; span=(0, 69), match='fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clust>
>>> text = 'fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clusterb{1,8}.example.com '
>>> re.match('^([a-z.-]+|{[\d,]+})+(,([a-z.-]+|{[\d,]+})+)*\s*$', text)
    <_sre.SRE_Match object; span=(0, 70), match='fe{10,20}.clustera{1,2}.example.com,fe{1,5}.clust>

快速匹配。在您的原始输入中,我不确定它是否可以找到匹配 - 它将根据规则进行详尽搜索,直到所有可能性都用完为止,然后将无法匹配。

具体的规则是什么规则?如果使用re.DEBUG标志编译正则表达式,则可以查看它们:

>>> re.compile('^([a-z.-]+|{[\d]+(,[\d]+)*})+(,([a-z.-]+|{[\d]+(,[\d]+)*})+)*$', re.DEBUG)
at at_beginning
max_repeat 1 4294967295
  subpattern 1
    branch
      max_repeat 1 4294967295
        in
          range (97, 122)
          literal 46
          literal 45
    or
      literal 123
      max_repeat 1 4294967295
        in
          category category_digit
      max_repeat 0 4294967295
        subpattern 2
          literal 44
          max_repeat 1 4294967295
            in
              category category_digit
      literal 125
max_repeat 0 4294967295
  subpattern 3
    literal 44
    max_repeat 1 4294967295
      subpattern 4
        branch
          max_repeat 1 4294967295
            in
              range (97, 122)
              literal 46
              literal 45
        or
          literal 123
          max_repeat 1 4294967295
            in
              category category_digit
          max_repeat 0 4294967295
            subpattern 5
              literal 44
              max_repeat 1 4294967295
                in
                  category category_digit
          literal 125
at at_end
    re.compile(r'^([a-z.-]+|{[\d]+(,[\d]+)*})+(,([a-z.-]+|{[\d]+(,[\d]+)*})+)*$',
re.UNICODE|re.DEBUG)

如果它显示literal <num>,您可以在ascii或unicode点表中找到它转换为的内容,例如在asciitable.com找到的内容。

如果你可以看到这里有两个巨大的循环,第一个max_repeat和第二个max_repeat,每个包含许多子循环/搜索。正则表达式引擎正在搜索这个的排列以尝试找到匹配。如果您可以对re.DEBUG返回的操作规则进行一些推理,它可以帮助您了解正则表达式引擎可能正在做什么。

答案 1 :(得分:1)

有一些确定的性能错误。这个特别的一个因为拥有&#39; $&#39;而且更加严重。在你的模式的最后。如果你删除它,那么匹配将很快完成,然后你可以手动确定它是否一直到达行/字符串的末尾。

如果您有时间,可能需要获取Python的最新测试版,并确保该错误存在然后进行报告。不久前I reported one,他们做得更好。