【问题标题】:Why does re.sub replace the entire pattern, not just a capturing group within it?为什么 re.sub 替换整个模式,而不仅仅是其中的一个捕获组?
【发布时间】:2017-06-25 13:32:42
【问题描述】:

re.sub('a(b)','d','abc') 产生 dc,而不是 adc

为什么re.sub 替换整个捕获组,而不只是捕获组'(b)'?

【问题讨论】:

  • 你没有在替换部分使用它,所以你期望什么?如果要替换前面有“a”的“b”,则需要re.sub('ab','ad','abc')re.sub('(a)b',r'\1d','abc'),其中"\1" 指的是捕获组。
  • 谢谢!预计默认情况下会替换捕获组。正确的方法看起来不那么直观,但可能更灵活。
  • @Nick:但是re.sub 文档说它确实做到了这一点,没有提到捕获组:"替换最左边的非重叠模式在字符串中"

标签: python regex python-3.x replace capturing-group


【解决方案1】:

因为它应该替换整个出现的模式:

返回通过替换repl替换字符串中最左边不重叠的模式获得的字符串。

如果只替换一些子组,那么具有多个组的复杂正则表达式将不起作用。有几种可能的解决方案:

  1. 完整指定模式:re.sub('ab', 'ad', 'abc') - 我的最爱,因为它非常易读且明确。
  2. 捕获您想要保留的组,然后在模式中引用它们(注意它应该是原始字符串以避免转义):re.sub('(a)b', r'\1d', 'abc')
  3. 与上一个选项类似:提供一个回调函数作为repl 参数,并使其处理Match 对象并返回所需的结果。
  4. 使用不包含在匹配中但会影响匹配的lookbehinds/lookaheds:re.sub('(?<=a)b', r'd', 'abxb') 产生adxb。该组开头的?<= 表示“这是一个前瞻”。

【讨论】:

  • 简单提示:您可以在 在您的正则表达式中使用\1re.match(r'([la]{2})-\1', 'la-la')。它会匹配引用的组(1 在这种情况下)匹配(不是模式),所以这个正则表达式不会匹配 la-al 例如。
【解决方案2】:

因为这正是re.sub() doc 告诉你它应该做的事情:

  • 模式'a(b)' 表示“匹配'a',带有可选的尾随'b'”。 (它可以自己匹配 'a',但它不可能像您期望的那样自己匹配 'b'。如果您是这个意思,请使用非贪婪的 (a)??b)。
  • 替换字符串是'd'
  • 因此,在您的字符串 'abc' 上,它匹配所有 'ab' 并将其替换为 'd',因此结果为 'dc'

如果你想要你想要的输出,你需要在 '(a)??' 上进行非贪婪匹配:

>>> re.sub('(a)??b','d','abc')
'dc'

【讨论】:

  • @Basj:我们多次询问 OP,据我所知,他们只想解释为什么输出中不存在捕获组,而不是修复。
  • @Basj:正如您从 cmets 看到的那样,我们中的一些人 4 年来一直在向 OP 询问他们想要什么。他们从来没有说过BADOUTPUT/GOODOUTPUT,那是你的标签。他们要求解释为什么它会以这种方式工作。我回答了。我什至告诉他们如何使用一个可能的正则表达式来获得他们想要的东西 - 请参阅我的最后一行。
【解决方案3】:

我知道这不是严格回答 OP 问题,但这个问题可能很难用谷歌搜索(被 \1 解释淹没......)

对于那些喜欢我的人来说,他们实际上想用字符串替换不是第一个捕获组,而无需特别了解字符串或正则表达式:

#find offset [start, end] of a captured group within string
r = regex.search(oldText).span(groupNb)
#slice the old string and insert replacementText in the middle 
newText = oldText[:r[0]] + replacementText + oldText[r[1]:]

我知道这是想要的行为,但我仍然不明白为什么 re.sub 不能指定它应该替换的实际捕获组......

【讨论】:

    【解决方案4】:
    import re
    
    pattern = re.compile(r"I am (\d{1,2}) .*", re.IGNORECASE)
    
    text = "i am 32 years old"
    
    if re.match(pattern, text):
        print(
            re.sub(pattern, r"Your are \1 years old.", text, count=1)
        )
    

    如上所述,首先我们编译一个带有不区分大小写标志的正则表达式模式。

    然后我们检查文本是否与模式匹配,如果匹配,我们引用正则表达式模式中唯一的组(年龄),组号为 \1。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-04
      • 1970-01-01
      • 1970-01-01
      • 2015-01-17
      • 2014-05-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多