正则表达式:匹配空字符串而不是空字符串

时间:2019-09-11 15:19:35

标签: regex

我有一个Python脚本,试图在其中解析以下形式的字符串:

one[two=three].four

每个单词都应该在其自己的捕获组中。标点符号不应被捕获。

此外,字符串的每个部分都是可选的,并且由方括号分隔的部分可以重复。因此,以上是最完整的示例,但以下所有内容也应该是有效的匹配项:

one
.four
one[two=three][five=six]
[two=three]
[two].four
[two][five]
[]

如果其中一个单词不存在,那么我想捕获一个长度为0的字符串,而不是无法捕获。

我正在使用的正则表达式如下:

pattern = re.compile(
    r"""
      ^                     # Assert start of string
      (?P<cap1>             # Start a new group for "one"
        [a-z]*              #
      )                     #
      (?:                   # Start a group for "two" and "three"
        \[                  # Match the "["
        (?P<cap_2>          # Start a group for "two"
          [a-z]*            #
        )                   #
        =?                  # Delimit two/three with "="
        (?P<cap_3>          # Start a group for "three"
          [a-z]*            #
        )                   #
        \]                  # Match the "]"
      )*                    # End the two-three group, allowing repeats
      \.?                   # Delimit three/four with "."
      (?P<cap_4>            # Begin a group for "four"
        [a-z]*              #
      )                     #
      $                     # Assert end of string
    """, re.IGNORECASE|re.VERBOSE)

我在正则表达式中尝试做的是,不是通过将?附加到整个组中来允许一个组中的0或1,而是允许任意数量的字符通过以下方式实际匹配将*附加到字符选择中。因此,该匹配项被强制存在,但字符串本身的长度可以为0。

问题来自方括号内。我使用的package允许我使用match.captures(groupname)访问命名组的所有捕获。这样,我可以使用cap_2访问match.captures("cap_2")的所有匹配项:

>>> pattern.match("one[two=three][five=six].four").captures("cap_2")
["two", "five"]

使用方括号时,此方法工作正常。但是,当他们不在时:

>>> pattern.match("one.four").captures("cap_2")
[]
Expected: [""]

我希望cap_2cap_3至少存在一个空字符串。但是,什么都没有。

这是因为我将*放在正则表达式的2 + 3部分之后,以便允许其中的多个组-这允许完全跳过正则表达式的那部分。

*更改为+会破坏正则表达式,因为现在它与上面的示例完全不匹配,因为它试图匹配方括号。在每个方括号后面添加?表示cap_1cap_2没有定界,并包括cap_4cap_3中应包含的内容。

这里的解决方案是什么?如何允许包含两个捕获组的组被多次执行,但是当不存在方括号时仅匹配空字符串?

2 个答案:

答案 0 :(得分:1)

您可以通过在*重复的(?:\[(?P<cap_2>[a-z]*)=?(?P<cap_3>[a-z]*)\])*组之后替换+并添加第二次出现的cap_2和{{1} }(请注意,PyPi regex module在同一个正则表达式中支持多个名称相同的组):

cap_3

请参见Python demo

问题是,import regex as re s = 'one.four' pattern = re.compile( r""" ^ # Assert start of string (?P<cap1> # Start a new group for "one" [a-z]* # ) # (?: (?: # Start a group for "two" and "three" \[ # Match the "[" (?P<cap_2> # Start a group for "two" [a-z]* # ) # =? # Delimit two/three with "=" (?P<cap_3> # Start a group for "three" [a-z]* # ) # \] # Match the "]" )+ # End the two-three group, allowing repeats | (?P<cap_2>)(?P<cap_3>) ) \.? # Delimit three/four with "." (?P<cap_4> # Begin a group for "four" [a-z]* # ) # $ # Assert end of string """, re.IGNORECASE|re.VERBOSE) print ( pattern.match("one.four").captures("cap_2") ) # => [''] 部分可以匹配,因为它可以匹配一个空字符串,并且如果仅添加替代项而不更改修饰符,则将无法获得预期的结果。因此,如果没有(?:\[(?P<cap_2>[a-z]*)=?(?P<cap_3>[a-z]*)\])*,则具有空模式的第二个[...]cap_2组将完全匹配以捕获空字符串。

答案 1 :(得分:0)

  • 如果希望它匹配空字符串或其他内容,则需要使用OR运算符:|
  • 如果您想让正则表达式匹配空字符串,则需要匹配空字符串的内容: ()(not empty|)

结合并应用于您的案例,看起来像这样(简化):

((?:\[stuff inside the brackets\])+|)

最外面的组将捕获整个方括号结构(例如[two][three])(如果存在)或为空字符串。请注意,|运算符的左侧现在必须至少匹配一次(+)。