TL;DR:如何让re.sub打印出它所做的替换,包括当使用组时?

有点像有一个详细的选项,有没有可能有re.sub打印出一个消息,每次它作出替换?这对于测试多行re.sub行如何与大文本交互非常有用.

利用repl参数可以是一个函数这一事实,我设法为简单的替换想出了这个解决方法:

import re

def replacer(text, verbose=False):
    def repl(matchobj, replacement):
        if verbose:
            print(f"Replacing {matchobj.group()} with {replacement}...")
        return replacement
    text = re.sub(r"[A-Z]+", lambda m: repl(m, "CAPS"), text)
    text = re.sub(r"\d+", lambda m: repl(m, "NUMBER"), text)
    return text

replacer("this is a 123 TEST 456", True)

# Log:
#   Replacing TEST with CAPS...
#   Replacing 123 with NUMBER...
#   Replacing 456 with NUMBER...

然而,这对组不起作用——似乎re.sub自动转义返回值repl:

def replacer2(text, verbose=False):
    def repl(matchobj, replacement):
        if verbose:
            print(f"Replacing {matchobj.group()} with {replacement}...")
        return replacement
    text = re.sub(r"([A-Z]+)(\d+)", lambda m: repl(m, r"\2\1"), text)
    return text

replacer2("ABC123", verbose=True) # returns r"\2\1"

# Log:
#   Replacing ABC123 with \2\1...

当然,可以编写一个更复杂的repl函数来实际判断replacement中的组,但在这一点上,对于仅仅让re.sub报告替换的目标来说,这个解决方案似乎太复杂了.另一个可能的解决方案是只使用re.search,报告它,然后使用re.sub来进行替换,可能使用Pattern.sub的变体来指定posendpos,以节省sub函数不必再次搜索整个字符串.当然有比这两种 Select 更好的方法吗?

推荐答案

使用matchobj.expand(replacement),它将处理替换字符串并进行替换:

import re

def replacer2(text, verbose=False):
    def repl(matchobj, replacement):
        result = matchobj.expand(replacement)
        if verbose:
            print(f"Replacing {matchobj.group()} with {result}...")
        return result
    text = re.sub(r"([A-Z]+)(\d+)", lambda m: repl(m, r"\2\1"), text)
    return text

print(replacer2("ABC123", verbose=True)

输出:

Replacing ABC123 with 123ABC...
123ABC

一个带有详细选项的扩展re.sub的通用示例,并允许替换函数使用组模式:

import re

def sub2(pattern, repl, string, count=0, flags=0, verbose=False):
    def helper(match, repl):
        result = match.expand(repl(match) if callable(repl) else repl)
        if verbose:
            print(f'offset {match.start()}: {match.group()!r} -> {result!r}')
        return result
    return re.sub(pattern, lambda m: helper(m, repl), string, count, flags)

# replace three digits with their reverse
print(sub2(r'(\d)(\d)(\d)', r'\3\2\1', 'abc123def45ghi789', verbose=True))
# replace three digits with their reverse, and two digits wrap with parentheses
print(sub2(r'(\d)(\d)(\d)?',
           lambda m: r'(\1\2)' if m.group(3) is None else r'\3\2\1', 
           'abc123def45ghi789', verbose=True))

输出:

offset 3: '123' -> '321'
offset 14: '789' -> '987'
abc321def45ghi987
offset 3: '123' -> '321'
offset 9: '45' -> '(45)'
offset 14: '789' -> '987'
abc321def(45)ghi987

Python相关问答推荐

如何使用symy打印方程?

如何比较numPy数组中的两个图像以获取它们不同的像素

'discord.ext. commanders.cog没有属性监听器'

如何从.cgi网站刮一张表到rame?

转换为浮点,pandas字符串列,混合千和十进制分隔符

使用Python从URL下载Excel文件

为什么\b在这个正则表达式中不解释为反斜杠

Flash只从html表单中获取一个值

如何防止Pandas将索引标为周期?

关于两个表达式的区别

Beautifulsoup:遍历一个列表,从a到z,并解析数据,以便将其存储在pdf中.

在极点中读取、扫描和接收有什么不同?

如何将相同组的值添加到嵌套的Pandas Maprame的倒数第二个索引级别

如何将泛型类类型与函数返回类型结合使用?

为什么dict. items()可以快速查找?

Python日志(log)库如何有效地获取lineno和funcName?

删除Dataframe中的第一个空白行并重新索引列

Polars时间戳同步延迟计算

如何批量训练样本大小为奇数的神经网络?

基于2级列表的Pandas 切片3级多索引