我有一个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_2
和cap_3
至少存在一个空字符串。但是,什么都没有。
这是因为我将*
放在正则表达式的2 + 3部分之后,以便允许其中的多个组-这允许完全跳过正则表达式的那部分。
将*
更改为+
会破坏正则表达式,因为现在它与上面的示例完全不匹配,因为它试图匹配方括号。在每个方括号后面添加?
表示cap_1
和cap_2
没有定界,并包括cap_4
中cap_3
中应包含的内容。
这里的解决方案是什么?如何允许包含两个捕获组的组被多次执行,但是当不存在方括号时仅匹配空字符串?
答案 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)
|
()
或(not empty|)
结合并应用于您的案例,看起来像这样(简化):
((?:\[stuff inside the brackets\])+|)
最外面的组将捕获整个方括号结构(例如[two][three]
)(如果存在)或为空字符串。请注意,|
运算符的左侧现在必须至少匹配一次(+
)。