如何仅对未被前后时间子串包围的日期字符串执行替换?
问题描述
需求:仅对未被hh:mm am/pm格式时间前后跟随的yyyy_-_mm_-_dd格式日期,替换为(日期 00:00 am);前后带有该格式时间的日期不替换。但现有正则实现错误修改了带前后时间的日期,导致输出不符合预期。
现有代码
import re, datetime 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 (2022_-_02_-_18 20:16 pm) a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)" print(repr(input_text)) # --> output 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"(\b\d{2}:\d{2}[\s|]*[ap]m)?" #to identify if there is a time after the date identify_only_date_regex_01 = r"(\b\d{2}:\d{2}[\s|]*[ap]m)?" + r"[\s|]*" + input_date_structure #to identify if there is a time before the date date_restructuring_structure = r"\g<year>_-_\g<month>_-_\g<startDay>" restructuring_only_date = lambda x: x.group() if x.group(1) else "(" + fr"{x.expand(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(2022_-_02_-_18 00:00 am) llega el avion, pero no ((2022_-_02_-_18 00:00 am) 20:16 pm) a las ((2022_-_02_-_18 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 (2022_-_02_-_18 20:16 pm) a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)"
规则示例
- 不替换:
"sdsdds 2022_-_02_-_18 00:16 am sdsddssd2"(日期后带时间) - 不替换:
"sdsdsd 00:16 am 2022_-_02_-_18 sdsdsd"(日期前带时间) - 替换:
"sdsdds 2022_-_02_-_18 sdsdsd"(孤立日期)
问题原因
- 正则逻辑反向:原正则用可选捕获组
(...)?"尝试匹配时间,意味着即使存在前后时间,正则仍会忽略时间、单独匹配日期部分(可选组允许匹配为空),导致带时间的日期被错误替换。 - 两次替换重复干扰:先替换“日期后可能带时间”的场景,再替换“日期前可能带时间”的场景,第二次替换会错误匹配已处理的内容,比如把
(2022_-_02_-_18 20:16 pm)中的日期单独揪出来替换。 - 未明确排除时间场景:原正则没有主动排除“日期前后存在有效时间”的情况,只是被动尝试匹配时间,无法精准定位孤立日期。
修正方案
使用负向断言(反向否定预查+正向否定预查)精准匹配孤立日期,确保日期前后不存在目标格式的时间,单次替换即可完成需求。
修正后代码
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 (2022_-_02_-_18 20:16 pm) a las (2022_-_02_-_18 00:16 am), de esos hay dos (22)" # 定义日期结构:限定年份为4位数字,更符合yyyy格式 input_date_structure = r"(?P<year>\d{4})_-_(?P<month>\d{2})_-_(?P<startDay>\d{2})" # 单独提取时间格式正则,便于维护 time_pattern = r"\b\d{2}:\d{2}\s*[ap]m\b" # 核心正则:用负向断言排除前后带时间的日期 isolate_date_regex = re.compile( fr"(?<!{time_pattern}\s*){input_date_structure}(?!\s*{time_pattern})" ) # 替换函数 def replace_isolate_date(match): date_str = match.group() return f"({date_str} 00:00 am)" # 执行单次替换 output_text = isolate_date_regex.sub(replace_isolate_date, input_text) print(repr(output_text))
修正说明
- 负向断言精准过滤:
(?<!{time_pattern}\s*):反向否定预查,确保日期前面没有“时间+任意空白”的内容,排除日期前带时间的场景。(?!\s*{time_pattern}):正向否定预查,确保日期后面没有“任意空白+时间”的内容,排除日期后带时间的场景。
- 优化日期正则:将年份匹配从
\d*改为\d{4},避免匹配非4位数字的无效年份。 - 单次替换避免干扰:一次匹配所有符合条件的孤立日期,不会重复处理导致错误。
- 模块化设计:将时间格式抽离为单独变量,后续修改时间格式只需调整一处。
内容的提问来源于stack exchange,提问作者Matt095
相关产品推荐
相关产品推荐

