Python多行正则匹配交错结果:能否用单一模式匹配全部目标?
问题:单一正则匹配所有已处理的txt文件
需求:匹配所有同时存在found、startprocess、endprocess记录的xxx.txt文件,示例文本如下:
texte=""" ..................................................................... ... ... found: a_dir/a_sub_dir/aaa.txt ..................................................................... ... ... found: a_dir/a_sub_dir/bbb.txt ..................................................................... ... ... startprocess: aaa.txt ..................................................................... ... ... found: a_dir/a_sub_dir/ccc.txt ..................................................................... ... ... startprocess: bbb.txt ..................................................................... ... ... endprocess: aaa.txt ..................................................................... ... endprocess: bbb.txt """
首次尝试:单一正则失效
使用以下正则尝试一次性匹配:
pattern=r' .*?(found:\s+.*?((?P<filename>[a-z]+\.txt)))(?:.*?)(?P<start>startprocess:\s+(?P=filename))(?:.*?)(?P<end>endprocess:\s+(?P=filename))' for m in re.finditer(pattern,texte, re.DOTALL): print(m.groupdict())
结果仅匹配到aaa.txt,遗漏了bbb.txt:
{'filename': 'aaa.txt', 'start': 'startprocess: aaa.txt', 'end': 'endprocess: aaa.txt'}
替代方案:循环匹配
先提取所有被发现的文件名,再逐个匹配对应流程:
list=[] pattern_template=r'.*?(found:\s+.*?(?P<filename>@filename@))(?:.*?)(?P<start>startprocess:\s+(?P=filename))(?:.*?)(?P<end>endprocess:\s+(?P=filename))' for filename in re.findall(r'(?:.*?)found:\s+.*?([a-z]+\.txt)',texte): pattern=pattern_template.replace('@filename@', filename) for m in re.finditer(pattern,texte, re.DOTALL): list.append(m.groupdict()) print(list)
结果成功匹配两个符合条件的文件:
[{'filename': 'aaa.txt', 'start': 'startprocess: aaa.txt', 'end': 'endprocess: aaa.txt'}, {'filename': 'bbb.txt', 'start': 'startprocess: bbb.txt', 'end': 'endprocess: bbb.txt'}]
核心疑问:是否存在单一正则模式,能一次性匹配所有符合条件的文件记录?
回答
可以用单一正则实现一次性匹配,关键是通过负向预查限制匹配范围,避免跨文件的内容干扰。
可行正则写法
import re texte=""" ..................................................................... ... ... found: a_dir/a_sub_dir/aaa.txt ..................................................................... ... ... found: a_dir/a_sub_dir/bbb.txt ..................................................................... ... ... startprocess: aaa.txt ..................................................................... ... ... found: a_dir/a_sub_dir/ccc.txt ..................................................................... ... ... startprocess: bbb.txt ..................................................................... ... ... endprocess: aaa.txt ..................................................................... ... endprocess: bbb.txt """ pattern = r'found:\s+.*?(?P<filename>[a-z]+\.txt)(?:(?!found:).)*?(?P<start>startprocess:\s+(?P=filename))(?:(?!found:).)*?(?P<end>endprocess:\s+(?P=filename))' matches = re.finditer(pattern, texte, re.DOTALL) result = [m.groupdict() for m in matches] print(result)
运行结果
[{'filename': 'aaa.txt', 'start': 'startprocess: aaa.txt', 'end': 'endprocess: aaa.txt'}, {'filename': 'bbb.txt', 'start': 'startprocess: bbb.txt', 'end': 'endprocess: bbb.txt'}]
正则逻辑说明
(?:(?!found:).)*?:这是负向预查结构,含义是"匹配任意字符,但只要下一个字符序列不是found:就继续",确保每个匹配块只包含单个文件的完整流程记录,不会覆盖或跳过后续文件的内容。- 首次尝试失败的原因:原正则中的
.*?会从文本开头开始匹配,吃掉aaa.txt的所有相关内容后,剩余文本里bbb.txt的found记录无法满足正则从开头匹配的逻辑,导致遗漏。而负向预查限制匹配范围后,每个匹配都独立对应一个文件的流程,不会互相干扰。
内容的提问来源于stack exchange,提问作者Frederic Faure
相关产品推荐
相关产品推荐

