re.match输出不确定

时间:2018-01-07 22:37:09

标签: python regex django

我正在使用re.match与python进行相当复杂的匹配,其形式为(some_pattern_1)?(some_pattern_2)?..(.*)

另一方面,我有一个单元测试,我正在检查大约一百个例子,它们都是异步向我的(本地,开发)服务器发送请求。服务器在django。

我有时候看到这场比赛显然是非贪婪的(即最后一次抓住所有区块的事情太多了)并且单元测试失败了,但是不能真正重现它,我不是真的知道发生了什么。

更具体地说,正则表达式的相关部分是(在Python中):

import re

input = "1 small shoe"

sizes = ["small", "large", "big", "huge"]
colors = ["blue", "red", "green", "yellow", "grey"]
anySize = u' |'.join(sizes)
anyColor = u' |'.join(colors)
matched_expression = re.match(
    r'\s*(?P<amount>(((\d{1,2}\.)?\d{1,3})?)\s*'
    r'(?P<size>(\b'+anySize+'\b)?)\s*'
    r'(?P<color>(\b'+anyColor+'\b)?)\s*'
    r'(?P<name>.*, input, re.UNICODE|re.IGNORECASE)
if matched_expression:
    print(matched_expression.groupdict()["amount"])
    print(matched_expression.groupdict()["size"])
    print(matched_expression.groupdict()["color"])
    print(matched_expression.groupdict()["name"])

我有时会看到这张照片:

1
''
''
'small shoe'

是否存在可能发生这种情况的条件(我认为正则表达式匹配原则上是完全确定的是正确吗?)

1 个答案:

答案 0 :(得分:1)

您用于构建模式的大多数字符串文字是原始文字(使用r前缀引入),这很棒 - 字符串解释器因此不提供反斜杠任何特殊的意义,但相反,它们完整的正则表达式解析器。但是,遗憾的是,在每种情况下都没有使用原始文字:

    r'(?P<size>(\b'+anySize+'\b)?)\s*'
#                           ^^^^^^^^^^   this is not a raw string literal

    r'(?P<color>(\b'+anyColor+'\b)?)\s*'
#                             ^^^^^^^^^^ and nor is this

因此,在将解释的字符串提供给正则表达式编译器之前,这些文字中的反斜杠具有String and Bytes literals下描述的效果。因此,您的\b边界锚将替换为ASCII退格字符!

使用原始字符串文字,前缀为r,或者确保转义它们包含的反斜杠。

但是,您的代码还有许多其他问题值得注意:

  1. 如目前所写,由于某些语法错误,您的正则表达式将无法编译。特别是,由于括号不平衡,名为amountname的捕获组不会终止:

        r'\s*(?P<amount>(((\d{1,2}\.)?\d{1,3})?)\s*'
    #        +          +++         -        - -
    

    有四个左括号,但只有三个右括号。你可能打算写:

        r'\s*(?P<amount>(((\d{1,2}\.)?\d{1,3})?))\s*'
    #                                           ^
    

    同样,r'(?P<name>.*, ...应该是r'(?P<name>.*)', ...(注意模式字符串需要在参数分隔符之前终止)。

  2. \b边界锚点比|交替更紧密地绑定,因此当放置在与连接数组相同的级别时,它们仅分别绑定到备选项的第一个和最后一个元素。例如,名为size的捕获组当前由以下模式指定:

    (\bsmall |large |big |huge\b)?
    

    在优先级方面,这相当于:

    ((\bsmall )|(large )|(big )|(huge\b))?
    

    最好将边界锚放在括号之外:

        r'(?P<size>\b('+anySize+r')?\b)\s*'
        r'(?P<color>\b('+anyColor+r')?\b)\s*'
    
  3. 如上所示,连接表达式中的空格可能会导致意外后果:anySizeanyColor要求除了基础数组中的最终项之外的所有项都是(如果存在) ,后跟一个空格字符(除了那些与\s*模式匹配的字符。最好单独使用'|'加入数组,而不是' |'

    anySize = u'|'.join(sizes)
    anyColor = u'|'.join(colors)
    
  4. 根据底层数组的来源以及您对它们的信心,它们不包含任何特殊的正则表达式模式,您可能希望首先转义数组元素。