如何避免被前瞻分隔的两个正则表达式匹配结果拆分?
问题解决:合并正则表达式的两个独立匹配结果
问题描述
我编写了一段正则表达式,意图匹配文本开头的可选内容,跳过中间部分后再匹配末尾的可选内容:
([A-Z]{1,3}[a-z]{1,3}\.\-?\s?)*(\s?\([A-Z]{2}\)\s)?([A-Z]{2}\s)?)?(?![A-Z][a-z]\s]+)([A-Z]{3}\s?)*
简化结构为:
somestuff?(?![A-Z][a-z]\s]+)someotherstuff?
单独匹配某一部分或都不匹配时均正常,但在Python中同时匹配前后两部分时,会得到两个独立的匹配结果,而非合并后的单一结果。例如测试代码:
import re regex = r"(([A-Z]{1,3}[a-z]{1,3}\.\-?\s?)*(\s?\([A-Z]{2}\)\s)?([A-Z]{2}\s)?)?(?![A-Z][a-z]\s]+)([A-Z]{3}\s?)*" test_str = "Test.-Ing. (XX) Foo Bar YYY DDD\n" matches = re.finditer(regex, test_str, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): print ("Match {matchNum}: {match}".format(matchNum = matchNum, match = match.group().strip()))
运行结果:
Match 1: Test.-Ing. Match 2: YYY DDD
期望结果:
Match 1: Test.-Ing. YYY DDD
尝试将整个表达式用括号包裹无效,需解决如何将两部分合并为同一个匹配结果的问题。
补充示例输入与期望输出:
输入:
Test.-Ing. (XX) Foo Bar YYY DDD Test.Ing. Foo Bar Foo Bar DDD Foo Bar DDD YYY Dr. Foo Bar Test.-Ing. Foo Bar Test. Foo Bar XX Foo Bar Foo F. Bar
期望输出(排除中间的Foo/Bar):
Test.-Ing. (XX) YYY DDD Test.Ing. DDD DDD YYY Dr. Test.-Ing. Test. XX 无输出
问题根源
原正则中的(?![A-Z][a-z]\s]+)是负前瞻断言,仅做条件检查不占用字符。匹配完开头部分后,引擎会在当前位置继续寻找末尾部分的匹配,导致生成两个独立的结果;同时正则未将中间需要跳过的内容(如Foo/Bar这类单词)纳入匹配范围,进一步拆分了匹配片段。
解决方案
将整行内容纳入匹配范围,用非捕获组匹配中间需要跳过的单词,同时用捕获组保留开头和末尾的目标内容,最后在代码中合并有效捕获结果。
修正后的正则与代码
import re def process_line(line): # 匹配包含中间跳过内容的场景 regex_with_middle = r"^((?:[A-Z]{1,3}[a-z]{1,3}(?:\.-)?\s*)*(?:\s?\([A-Z]{2}\)\s)?(?:[A-Z]{2}\s)?)(?:\b[A-Z][a-z]+\b\s*)+((?:[A-Z]{3}\s*)*)$" match = re.match(regex_with_middle, line.strip()) if match: part1 = match.group(1).strip() part2 = match.group(2).strip() return " ".join(filter(None, [part1, part2])) or "无输出" # 匹配无中间跳过内容的场景(仅开头或仅末尾) regex_no_middle = r"^((?:[A-Z]{1,3}[a-z]{1,3}(?:\.-)?\s*)*(?:\s?\([A-Z]{2}\)\s)?(?:[A-Z]{2}\s)?|(?:[A-Z]{3}\s*)*)$" match = re.fullmatch(regex_no_middle, line.strip()) if match: result = match.group(1).strip() return result if result else "无输出" # 不符合任何匹配规则 return "无输出" # 测试示例输入 test_lines = [ "Test.-Ing. (XX) Foo Bar YYY DDD", "Test.Ing. Foo Bar", "Foo Bar DDD", "Foo Bar DDD YYY", "Dr. Foo Bar", "Test.-Ing. Foo Bar", "Test. Foo Bar", "XX Foo Bar", "Foo F. Bar" ] for line in test_lines: print(process_line(line))
运行结果
Test.-Ing. (XX) YYY DDD Test.Ing. DDD DDD YYY Dr. Test.-Ing. Test. XX 无输出
内容的提问来源于stack exchange,提问作者topkek
相关产品推荐
相关产品推荐

