re.sub方法为何替换为捕获组名而非对应捕获值?
问题:Python re.sub处理日期字符串时捕获组未正确替换
需求说明
仅对未附带hh:mm am/pm时间标识的日期格式(如2022_-_02_-_18)进行改写,包裹为(日期 00:00 am)形式;已附带时间标识的日期保持原样不变。
原代码及问题
原测试代码尝试用re.sub结合捕获组和lambda函数处理,但替换时输出了\g<year>这类捕获组名称而非实际匹配值,且错误修改了带时间的日期:
import re, datetime # example input_text = "Alrededor de las 00:16 am o las 23:30 pm 2022_-_02_-_18 , quizas cerca del 2022_-_02_-_18 llega el avion, pero no a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)" # identification pattern input_date_structure = r"(?P<year>\d*)_-_(?P<month>\d{2})_-_(?P<startDay>\d{2})" identify_only_date_regex_00 = input_date_structure + r"[\s|]*" + r"(\d{2}:\d{2}[\s|]*[ap]m)?" # to identify if there is a time after the date identify_only_date_regex_01 = r"(\d{2}:\d{2}[\s|]*[ap]m)?" + r"[\s|]*" + input_date_structure # to identify if there is a time before the date # replacement structure date_restructuring_structure = r"\g<year>_-_\g<month>_-_\g<startDay>" restructuring_only_date = lambda x: x.group() if x.group(1) else " (" + date_restructuring_structure + " 00:00 am)" # do the replace with re.sub() method and the regex patterns instructions input_text = re.sub(identify_only_date_regex_00, restructuring_only_date, input_text) input_text = re.sub(identify_only_date_regex_01, restructuring_only_date, input_text) # print output print(repr(input_text)) # --> output
错误输出
'Alrededor de las 00:16 am o las 23:30 pm 2022_-_02_-_18 , quizas cerca del (\\g<year>_-_\\g<month>_-_\\g<startDay> 00:00 am) llega el avion, pero no a las ( (\\g<year>_-_\\g<month>_-_\\g<startDay> 00:00 am) 00:16 am), de esos hay dos (22)'
期望输出
"Alrededor de las 00:16 am o las 23:30 pm 2022_-_02_-_18 , quizas cerca del (2022_-_02_-_18 00:00 am) llega el avion, pero no a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)"
问题原因
- 捕获组语法使用错误:
\g<year>这类语法仅在re.sub的第二个参数为字符串时会被解析为捕获组内容;但在lambda函数中,直接拼接该字符串只会把它当作普通文本输出,不会替换为实际匹配值。 - 正则逻辑缺陷:原正则会匹配带时间的日期(因为时间部分是可选分组),且两次替换会重复处理已修改的内容(比如把带时间的日期也错误包裹)。
修正方案
关键修改点
- 用负向前后断言编写正则,确保仅匹配前后没有时间标识的独立日期,避免误匹配带时间的情况。
- 在lambda函数中直接通过匹配对象的
group()方法获取捕获组的实际值,而非使用\g<name>语法。 - 排除已被括号包裹的日期,避免重复处理。
修正后的代码
import re input_text = "Alrededor de las 00:16 am o las 23:30 pm 2022_-_02_-_18 , quizas cerca del 2022_-_02_-_18 llega el avion, pero no a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)" # 正则:匹配前后无时间、且未被括号包裹的独立日期 pattern = r""" (?<!\d{2}:\d{2}\s*[ap]m\s*) # 前面没有hh:mm am/pm格式的时间 (?<!\() # 前面没有左括号(避免匹配已包裹的日期) (?P<year>\d*)_-_(?P<month>\d{2})_-_(?P<startDay>\d{2}) (?!\s*\d{2}:\d{2}\s*[ap]m) # 后面没有hh:mm am/pm格式的时间 (?!\)) # 后面没有右括号 """ def replace_date(match): # 获取实际捕获的日期各部分 date_str = f"{match.group('year')}_-_{match.group('month')}_-_{match.group('startDay')}" return f"({date_str} 00:00 am)" # 使用VERBOSE模式让正则更易读,同时执行替换 output_text = re.sub(pattern, replace_date, input_text, flags=re.VERBOSE) print(repr(output_text))
输出结果
'Alrededor de las 00:16 am o las 23:30 pm 2022_-_02_-_18 , quizas cerca del (2022_-_02_-_18 00:00 am) llega el avion, pero no a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)'
内容的提问来源于stack exchange,提问作者Matt095
相关产品推荐
相关产品推荐

