空字符串而不是不匹配的组错误

Empty string instead of unmatched group error

我有这段代码:

for n in (range(1,10)):
    new = re.sub(r'(regex(group)regex)?regex', r'something'+str(n)+r'', old, count=1)

它抛出不匹配的组错误。但如果它不匹配,我想在那里添加空字符串而不是抛出错误。我怎样才能做到这一点?

注意:我的完整代码比这个例子复杂得多。但是,如果您找到更好的解决方案如何遍历匹配项并在其中添加数字,您可以分享。我的完整代码:

for n in (range(1,(text.count('soutez')+1))):
    text = re.sub(r'(?i)(\s*\{{2}infobox medaile reprezentant(ka)?\s*\|\s*([^\}]*)\s*\}{2}\s*)?\{{2}infobox medaile soutez\s*\|\s*([^\}]*)\s*\}{2}\s*', r"\n | reprezentace"+str(n)+r" = \n | soutez"+str(n)+r" = \n | medaile"+str(n)+r" = \n", text, count=1)

根本原因

在 Python 3.5 之前,Python re.sub 中对失败捕获组的反向引用未填充空字符串。这里是Bug 1519638 description at bugs.python.org。因此,当对未参与比赛的组使用反向引用时会导致错误。

有两种方法可以解决该问题。

解决方案 1:添加空替代项以使可选组成为必需项

您可以将所有可选的捕获组(那些构造如 (\d+)?)替换为具有空替代项(即 (\d+|))的强制性捕获组。

这里是an example of the failure:

import re
old = 'regexregex'
new = re.sub(r'regex(group)?regex', r'somethingsomething', old)
print(new)

Replacing one line

new = re.sub(r'regex(group|)regex', r'somethingsomething', old)

有效。

解决方案 2:在替换中使用 lambda 表达式并检查组是否 None

如果您在另一个可选组中有可选组,则此方法是必要的。

您可以在替换部分使用 lambda 来检查组是否已初始化,而不是 None,使用 lambda m: m.group(n) or ''在您的案例中使用此解决方案,因为您在替换模式中有两个反向引用 - #3 和 #4,但是 some matches(请参阅匹配 1 和 3)没有捕获组 3 已初始化。发生这种情况是因为整个第一部分 - (\s*\{{2}funcA(ka|)\s*\|\s*([^}]*)\s*\}{2}\s*|) - 没有参与比赛,并且内部捕获组 3(即 ([^}]*))只是 即使在添加空之后也不会被填充替代.

re.sub(r'(?i)(\s*\{{2}funcA(ka|)\s*\|\s*([^\}]*)\s*\}{2}\s*|)\{{2}funcB\s*\|\s*([^\}]*)\s*\}{2}\s*', 
r"\n | funcA"+str(n)+r" = \n | funcB"+str(n)+r" = \n | string"+str(n)+r" = \n", 
text, 
count=1)

应该重写为

re.sub(r'(?i)(\s*{{funcA(ka|)\s*\|\s*([^}]*)\s*}}\s*|){{funcB\s*\|\s*([^}]*)\s*}}\s*', 
lambda m: r"\n | funcA"+str(n)+r" = " + (m.group(3) or '') + "\n | funcB" + str(n) + r" = " + (m.group(4) or '') + "\n | string" + str(n) + r" = \n", 
text, 
count=1)  

IDEONE demo

import re
 
text = r'''
 
{{funcB|param1}}
*some string*
{{funcA|param2}}
{{funcB|param3}}
*some string2*
 
{{funcB|param4}}
*some string3*
{{funcAka|param5}}
{{funcB|param6}}
*some string4*
'''
 
for n in (range(1,(text.count('funcB')+1))):
    text = re.sub(r'(?i)(\s*\{{2}funcA(ka|)\s*\|\s*([^\}]*)\s*\}{2}\s*|)\{{2}funcB\s*\|\s*([^\}]*)\s*\}{2}\s*', 
    lambda m: r"\n | funcA"+str(n)+r" = "+(m.group(3) or '')+"\n | funcB"+str(n)+r" = "+(m.group(4) or '')+"\n | string"+str(n)+r" = \n", 
    text, 
    count=1) 
    
assert text == r'''
| funcA1 =
| funcB1 = param1
| string1 =
*some string*
| funcA2 = param2
| funcB2 = param3
| string2 =
*some string2*
| funcA3 =
| funcB3 = param4
| string3 =
*some string3*
| funcA4 = param5
| funcB4 = param6
| string4 =
*some string4*
'''
print 'ok'

我又看了一遍
请注意,不幸的是你必须处理 NULL's,
但这里是你必须遵守的规则。

以下匹配全部成功,什么都不匹配。
你必须这样做才能找出规则。

这并不像你想象的那么简单。仔细看看结果。
没有明显的固定方式 formwise 来判断您将获得 NULL 还是 EMPTY。

不过仔细一看,规则出来了,还算简单。
如果您关心 NULL,则必须遵循这些规则。

只有两条条规则:

规则 # 1 - 任何无法到达的代码 GROUP 都将导致 NULL

   (?<Alt_1>                     # (1 start)
        (?<a> a )?                    # (2)
        (?<b> b? )                    # (3)
   )?                            # (1 end)
|  
   (?<Alt_2>                     # (4 start)
        (?<c> c? )                    # (5)
        (?<d> d? )                    # (6)
   )                             # (4 end)
 **  Grp 0         -  ( pos 0 , len 0 )  EMPTY 
 **  Grp 1 [Alt_1] -  ( pos 0 , len 0 )  EMPTY 
 **  Grp 2 [a]     -  NULL 
 **  Grp 3 [b]     -  ( pos 0 , len 0 )  EMPTY 
 **  Grp 4 [Alt_2] -  NULL 
 **  Grp 5 [c]     -  NULL 

规则 # 2 - 任何无法在 INSIDE 上匹配的代码组将导致 NULL

 (?<A_1>                       # (1 start)
      (?<a1> a? )                   # (2)
 )?                            # (1 end)
 (?<A_2>                       # (3 start)
      (?<a2> a )?                   # (4)
 )?                            # (3 end)
 (?<A_3>                       # (5 start)
      (?<a3> a )                    # (6)
 )?                            # (5 end)
 (?<A_4>                       # (7 start)
      (?<a4> a )?                   # (8)
 )                             # (7 end)
**  Grp 0       -  ( pos 0 , len 0 )  EMPTY 
**  Grp 1 [A_1] -  ( pos 0 , len 0 )  EMPTY 
**  Grp 2 [a1]  -  ( pos 0 , len 0 )  EMPTY 
**  Grp 3 [A_2] -  ( pos 0 , len 0 )  EMPTY 
**  Grp 4 [a2]  -  NULL 
**  Grp 5 [A_3] -  NULL 
**  Grp 6 [a3]  -  NULL 
**  Grp 7 [A_4] -  ( pos 0 , len 0 )  EMPTY 
**  Grp 8 [a4]  -  NULL 

简化:

问题

  1. 您从 Python 2.7 正则表达式中收到错误 "sre_constants.error: unmatched group"
  2. 你有任何带可选组的正则表达式模式(有或没有嵌套表达式),并试图在你的子 replacement 参数(re.sub(pattern, *repl*, string)compiled.sub(*repl*, string))

解决方案:

对于结果,return match.group(1) 而不是 </code>(或 2、3 等)。而已;不需要 <em> 或 </em>。组结果可以 return 使用函数或 lambda 编辑。</p> <h3>例子</h3> <p>您正在使用 <a href="">common regex to strip C-style comments</a>. Its design <a href="https://regex101.com/r/kB5kA4/1" rel="nofollow noreferrer">uses an optional group 1</a> 传递不应删除的伪评论(如果存在)。</p> <pre><code>pattern = r'//.*|/\*[\s\S]*?\*/|("(\.|[^"])*")' regex = re.compile(pattern)

使用 </code> 失败并出现错误:"sre_constants.error: unmatched group":</p> <pre><code>return regex.sub(r'', string)

使用.group(1)成功:

return regex.sub(lambda m: m.group(1), string)

对于那些不熟悉 lambda 的人,这个解决方案等同于:

def optgroup(match):
    return match.group(1)
return regex.sub(optgroup, string)

有关 为什么 </code> 由于 Bug 1519638 而失败的精彩讨论,请参阅已接受的答案。虽然已接受的答案是权威的,但它有两个缺点:1)原始问题中的示例非常复杂,以至于示例解决方案难以阅读,并且 2) 它建议 returning 一组 <strong> 或 </strong> 空字符串——这不是必需的,您可以只在每场比赛中调用 <code>.group()