正则表达式基础构件:看懂这几种写法就够用

正则表达式乍看像一串乱码,但只要意识到它其实是由为数不多的几种构件反复组合而成,读起来就会顺畅很多。

锚点(^ 和 $)用来固定匹配的位置

^匹配字符串(在多行模式下是每一行)的开头,$匹配结尾,不使用锚点时,模式可以匹配字符串中的任意位置,这是意外部分匹配最常见的原因。

字符类用来匹配一组候选字符中的一个

[abc]匹配a、b、c中的任意一个字符,[a-z]这样的范围写法以及\d(数字)、\w(单词字符)、\s(空白)等简写形式最为常用。

量词控制重复的次数

*表示0次或多次,+表示1次或多次,?表示0次或1次,{n,m}则指定一个具体的次数范围,它们都只作用于紧挨在前面的那一个元素。

圆括号同时承担分组和捕获两个作用

(模式)既能把一段子模式打包,让量词可以整体作用于它,也会把匹配到的文本保存下来供之后引用;如果只需要分组、不需要捕获,可以用(?:模式)。

贪婪匹配默认会尽可能匹配更长的内容

像.*这样的量词默认会尝试匹配尽可能长的内容,在后面加上?写成.*?就变成懒惰匹配(非贪婪),会尽可能匹配更短的内容,当字符串中同一个模式出现多次时,这个区别影响很大。

为什么同一个正则表达式在不同语言里表现不一样

PCRE、JavaScript的RegExp、Python的re等引擎虽然共享大部分基础语法,但在细节上有差异,比如\d是否只匹配ASCII数字,或者是否支持后行断言(lookbehind),因此在一个环境里测试通过的正则表达式,换到另一个语言里未必能得到完全相同的结果。

正则表达式擅长模式匹配,而不是真正的结构化解析

对于HTML、JSON这类真正结构化的格式,单纯用正则表达式处理,复杂度一旦上升就容易变得脆弱、难以维护。它真正擅长的是行级别、扁平结构的校验和提取,比如判断一个字符串是否符合邮箱的大致格式,或者从字符串中把数字部分抽取出来。

常见问题

为什么匹配到的内容比预期的要多?

通常是贪婪量词匹配了比预期更长的内容,可以尝试改成懒惰写法(*?或+?),或者用更精确的字符类代替范围过大的通配符。

捕获组和非捕获组有什么区别?

捕获组(模式)会把匹配到的文本保存下来,方便之后引用(比如在替换字符串里使用);非捕获组(?:模式)只影响分组和量词的作用范围,不会保存任何内容。