首页 > 解决方案 > re.sub 的 repl 表达式中不能调用 capture-group 上的函数?例如 int(r'\1')

问题描述

(这个问题的目的是找到一个优雅的 Python 习语来解决无法将捕获组引用传播到函数调用中的repl 表达式,re.sub()r'\1'int()例如。不要挂断特定parse_ampm示例,显然是 am-pm解析可以通过 stdlib 调用来完成,这不是重点)。

考虑在时间字符串中解析可选的“PM”'hPM''hhPM'(可选的分钟或秒,'h:mmPM''h:mm:ssPM')示例:'11', '11PM', '4:10', '4:10PM', '12:11:10PM'.

...并通过将小时转换为整数来替换它,添加 12,然后转换/格式化回字符串,并附加(可选)MM:SS。显然,这可以在 Python 中以各种粗鲁的方式完成,但显然不是简单的单行re.sub,因为repl-expression 只能处理直接的捕获组引用,例如r'\1',而不能将它们传递给类似的函数int()

BAD 1)re.sub()不会传播r'\1'到表达式中的函数调用int()repl

>>> re.sub(r'(\d+)(:\d+)?(PM)', lambda m: int(r'\1') + 12, '4:10PM')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '\\1'

re.subdoc 暗示(但从未声明)\1可以在 repl 表达式中使用,并且还明确表示repl 可以是字符串或函数”。但它从未明确表示 '\1' 不会被插值作为对 a 的反向引用当repl表达式是一个函数,而不是一个字符串时,捕获组;当repl是一个函数时,它只能使用group()Match对象的调用)

BAD 2)我们也不能re.sub在 f-string 中使用反向引用(Python 3.7),第二个原因:因为实际的 f-string 本身是在解释时构建的,而不是在运行时构建的:

>>> re.sub(r'(\d+)(:\d+)?(PM)', fr'{int(\1) + 12}\2', s2)
  File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash

解决方案3)笨重的分段方法

def parse_ampm(x):
    """Parse 'HH(:MM:SS)(AM/PM)' time-string, strip AM/PM, correct hour+12 for PM"""
    hmsn = list( re.match('(\d+)([:\d]*)?(?P<nm>AM|PM)?', x).groups() )
    hmsn[0] = int(hmsn[0])
    if (hmsn[-1] == 'PM') ^ (hmsn[0] == 12):
        hmsn[0] = (hmsn[0] + 12) % 24
    return f'{hmsn[0]}{hmsn[1]}'

    for s in ['4', '4:10', '4:10PM', '12:11:10PM']:
        parse_ampm(s)

解决方案 3b)另一种笨重的分段方法

if s.endswith('PM'):
    hms = s2[:-2].split(':') # optional MM:SS
    hms[0] = int(hms[0])+12
    s = ':'.join(str(x) for x in hms)

解决方案 4)基于 dict 的 hacky 方法将所有 12 个可能的小时值映射str(int(hour)+12)不需要整数运算,这看起来很丑陋和 hacky(但我想最快):

adjust_for_pm = {str(h):str(h+12) for h in range(1,12+1)}
adjust_for_pm.get(h)

笔记:

标签: pythonregexdatetime-formatsubstitution

解决方案


推荐阅读