正则表达式匹配严格按从左到右顺序逐字符试探,依赖引擎规则而非“最像”;贪婪量词靠回溯调整,分支|左优先,锚点控制匹配位置。
正则表达式匹配不是“挑最像的”,而是严格按书写顺序和引擎规则一步步试探出来的。理解这个顺序,才能写出稳定、可预期的正则。
引擎不会跳着看,它从正则开头第一个符号开始,逐个消费目标字符串的字符。比如 a.b 在 "axb" 中能匹配,是因为:
- 先匹配 a(位置 0)
- 再匹配任意非换行符(. 匹配 x)
- 最后匹配 b(位置 2)
任何一步失败,就回退或换起始位置重试。
*、+、{m,n} 默认尽可能多吃字符,但前提是整个模式能最终成立。吃多了导致后面无法匹配时,引擎会“吐出”字符再试——这就是回溯。
a.*b 在 "a123b456b" 中匹配整个串,因为 .* 先吞到末尾,再一点点吐,直到最后一个 b 能对上b?用非贪婪:a.*?b → 匹配 "a123b"
(a+)+
| 不是并行判断,而是“先试左边,成功就返回,右边根本不管”。这常导致意料之外的结果:
cat|caterpillar 在 "caterpillar" 中只匹配 "cat"(左边先成)caterpillar|cat 才会匹配完整单词(长分支放前)a|ab 在 "ab" 中只取 "a",剩下 "b" 被忽略——这不是 bug,是规则^、$、b 本身不占字符,但强制匹配必须发生在特定位置,从而改变实际生效的起点:
re.match(r"a.*b", "x a1b y") 返回 None,因为 match() 要求从开头匹配,而首字符是 "x"
re.search(r"baw*bb", "I have a1b and a2b.") 能分别找到两个单词,b 确保 a1b 不被当作 "have" 的一部分^ 和 $ 组合起来就是整行精确匹配:^abc$ 只认 "abc" 这一行