【问题标题】:Python regex instantly replace groups with group namesPython 正则表达式立即用组名替换组
【发布时间】:2016-08-24 23:30:24
【问题描述】:

以下回复:

import re
s = "the blue dog and blue cat wore 7 blue hats 9 days ago"
p = re.compile(r'blue (?P<animal>dog|cat)')
p.sub(r'\1',s)

结果,

'the dog and cat wore 7 blue hats 9 days ago'

是否可以编写一个 re.sub 使得:

import re
s = "the blue dog and blue cat wore 7 blue hats 9 days ago"
p = re.compile(r'blue (?P<animal>dog|cat)|(?P<numberBelowSeven>[0-7])|(?P<numberNotSeven>[8-9])')

结果,

'the animal and animal wore numberBelowSeven blue hats numberNotSeven days ago"

奇怪的是,replace strings galore 和 getting group names 上有文档,但不是一个有据可查的方式来做这两个。

【问题讨论】:

    标签: python regex replace


    【解决方案1】:

    你可以使用re.sub with a callback,它返回matchobj.lastgroup

    import re
    
    s = "the blue dog and blue cat wore 7 blue hats 9 days ago"
    p = re.compile(r'blue (?P<animal>dog|cat)|(?P<numberBelowSeven>[0-7])|(?P<numberNotSeven>[8-9])')
    
    def callback(matchobj):
        return matchobj.lastgroup
    
    result = p.sub(callback, s)
    print(result)
    

    产量

    the animal and animal wore numberBelowSeven blue hats numberNotSeven days ago
    

    请注意,如果您使用的是 Pandas,则可以使用 Series.str.replace

    import pandas as pd
    
    def callback(matchobj):
        return matchobj.lastgroup
    
    df = pd.DataFrame({'foo':["the blue dog", "and blue cat wore 7 blue", "hats 9", 
                              "days ago"]})
    pat = r'blue (?P<animal>dog|cat)|(?P<numberBelowSeven>[0-7])|(?P<numberNotSeven>[8-9])'
    df['result'] = df['foo'].str.replace(pat, callback)
    print(df)
    

    产量

                            foo                                 result
    0              the blue dog                             the animal
    1  and blue cat wore 7 blue  and animal wore numberBelowSeven blue
    2                    hats 9                    hats numberNotSeven
    3                  days ago                               days ago
    

    如果您有嵌套的命名组,您可能需要一个更复杂的回调,它遍历 matchobj.groupdict().items() 以收集所有相关的组名:

    import pandas as pd
    
    def callback(matchobj):
        names = [groupname for groupname, matchstr in matchobj.groupdict().items()
                 if matchstr is not None]
        names = sorted(names, key=lambda name: matchobj.span(name))
        result = ' '.join(names)
        return result
    
    df = pd.DataFrame({'foo':["the blue dog", "and blue cat wore 7 blue", "hats 9", 
                              "days ago"]})
    
    pat=r'blue (?P<animal>dog|cat)|(?P<numberItem>(?P<numberBelowSeven>[0-7])|(?P<numberNotSeven>[8-9]))'
    
    # pat=r'(?P<someItem>blue (?P<animal>dog|cat)|(?P<numberBelowSeven>[0-7])|(?P<numberNotSeven>[8-9]))'
    
    df['result'] = df['foo'].str.replace(pat, callback)
    print(df)
    

    产量

                            foo                                            result
    0              the blue dog                                        the animal
    1  and blue cat wore 7 blue  and animal wore numberItem numberBelowSeven blue
    2                    hats 9                    hats numberItem numberNotSeven
    3                  days ago                                          days ago
    

    【讨论】:

    • 这很好地回答了我的问题 - 谢谢!我遇到了一个意料之外的问题 - 我用来替换文本位的名称的组实际上是在一个组内。如果pat=r'(?P&lt;someItem&gt;blue (?P&lt;animal&gt;dog|cat)|(?P&lt;numberBelowSeven&gt;[0-7])|(?P&lt;numberNotSeven&gt;[8-9]))',如何修改它以使其以相同的方式工作?
    • 为了更好地使用我们已经拥有的示例,最好询问您的解决方案是否可以扩展以返回组名 numberItem 假设组 i> pat=r'blue (?P&lt;animal&gt;dog|cat)|(?P&lt;numberItem&gt;(?P&lt;numberBelowSeven&gt;[0-7])|(?P&lt;numberNotSeven&gt;[8-9]))'
    • 我添加了一个可以处理嵌套命名组的替代回调。
    【解决方案2】:

    为什么不多次调用re.sub()

    >>> s = re.sub(r"blue (dog|cat)", "animal", s)
    >>> s = re.sub(r"\b[0-7]\b", "numberBelowSeven", s)
    >>> s = re.sub(r"\b[8-9]\b", "numberNotSeven", s)
    >>> s
    'the animal and animal wore numberBelowSeven blue hats numberNotSeven days ago'
    

    然后您可以将其放入“更改列表”并一一应用:

    >>> changes = [
    ...     (re.compile(r"blue (dog|cat)"), "animal"),
    ...     (re.compile(r"\b[0-7]\b"), "numberBelowSeven"),
    ...     (re.compile(r"\b[8-9]\b"), "numberNotSeven")
    ... ]
    >>> s = "the blue dog and blue cat wore 7 blue hats 9 days ago"
    >>> for pattern, replacement in changes:
    ...     s = pattern.sub(replacement, s)
    ... 
    >>> s
    'the animal and animal wore numberBelowSeven blue hats numberNotSeven days ago'
    

    请注意,我还添加了单词边界检查 (\b)。

    【讨论】:

    • 因为我需要在 20 多个组上执行此操作,这些组在 200,000 行高的 pandas 数据框上共同搜索 40 多个术语
    • 他想让“蓝色动物”变成“动物”
    • @zelusp 好的,但您仍然可以这样做,列出模式和替换并迭代应用,在答案中添加示例。
    猜你喜欢
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 2015-07-25
    • 2012-08-01
    相关资源
    最近更新 更多