深色模式
正则表达式基础
概述
学正则时,最容易踩的坑不是“不会写复杂表达式”,而是连最基本的匹配单位都没分清。* 到底在重复什么,^ 和 $ 到底在匹配字符还是位置,很多混乱都从这里开始。
这一篇只讲大多数正则里都常见的基础能力,不急着区分 BRE、ERE、PCRE 或某门语言的特殊扩展。先把最稳定的那一层掌握住,后面再看方言差异会轻松很多。
普通字符和元字符
正则里最基本的单位是字符。普通字符通常按字面含义匹配自己:
text
cat这个模式最普通的理解,就是匹配连续出现的 c、a、t。
元字符则不同,它们不是匹配自己,而是参与描述规则。常见的元字符包括:
.:匹配一个任意字符*:重复前一个表达式零次或多次+:重复前一个表达式一次或多次?:重复前一个表达式零次或一次^:匹配开头位置$:匹配结尾位置
这里先记住一句话:正则不是按“单个符号”理解,而是按“前一个表达式”理解。后面看到 *、+、? 时都要先问,它修饰的是谁。
字符组
字符组用 [] 表示,意思是“这里可以是其中任意一个字符”。
text
[abc]它匹配的是一个字符,这个字符可以是 a、b、c 之一。
常见写法有这些:
[abc]:匹配a、b、c中任意一个[^abc]:匹配不是a、b、c的任意一个字符[0-9]:匹配一个数字[A-Za-z_]:匹配一个字母或下划线
字符组最大的作用,是把一堆“可选字符”压缩成一个位置上的规则。它和 (ab|cd) 不是一回事,前者匹配一个字符,后者匹配一个分支。
数量限定
数量限定符用来描述“前一个表达式出现几次”。
text
a*
ab+
[0-9]{4}这几种写法分别表示:
a*:a出现零次或多次ab+:a后面跟一个或多个b[0-9]{4}:连续四个数字
常见数量限定符如下:
| 写法 | 含义 |
|---|---|
* | 零次或多次 |
+ | 一次或多次 |
? | 零次或一次 |
{m} | 恰好 m 次 |
{m,} | 至少 m 次 |
{m,n} | m 到 n 次 |
很多人第一次写正则会把 * 理解成“任意多个字符”。这并不准确。.* 才比较接近“任意多个字符”,因为 . 先表示“任意一个字符”,* 再把它重复很多次。
位置锚点
^ 和 $ 最常见的作用不是匹配字符,而是匹配位置。
^abc:要求abc出现在开头abc$:要求abc出现在结尾^abc$:要求整段内容就是abc
这类写法在“判断格式是否完全匹配”时特别常见。比如校验四位数字:
text
^[0-9]{4}$如果没有 ^ 和 $,很多实现会把它理解成“只要某个位置出现四位数字就算匹配”,这和“整个字符串必须是四位数字”完全不是一回事。
分组、选择和转义
分组通常用 () 表示,作用有两个:
- 把多个字符组合成一个整体
- 让数量限定符作用在整个分组上
例如:
text
(ab){3}它匹配的是 ababab,不是 abbb。
选择通常写成 |,表示左右两边二选一:
text
cat|dog它匹配 cat 或 dog。
转义通常用 \,用来让本来有特殊含义的字符改回字面含义。例如要匹配字面上的点号,就不能直接写 .,而要写成 \.。
text
\.log$这个模式的重点不是“任意字符后接 log”,而是“字面上的 .log 结尾”。
读正则时的顺序
看一段正则时,按下面这个顺序理解通常比较稳:
- 先找锚点,判断它要匹配整段还是局部。
- 再找分组和字符组,判断每个位置能出现什么。
- 最后看数量限定符,确认谁被重复了几次。
比如这段:
text
^[A-Za-z_][A-Za-z0-9_]{2,15}$可以拆成这样理解:
^和$说明匹配整个字符串- 第一位必须是字母或下划线
- 后面跟 2 到 15 个字母、数字或下划线
这样看会比从左到右死抠符号容易得多。
几个特别常见的误解
.不一定真的等于“任意字符”。很多实现默认不匹配换行符。*、+、?修饰的是前一个表达式,不是整个模式。[]匹配一个字符,()|常常对应多个字符的分支。^、$说的是位置,不是普通字符本身。- 看见
\d、\w、环视、命名分组,不要立刻默认“所有正则都支持”。
基础层先掌握这些就够了。下一篇再把 POSIX 里的 BRE 和 ERE 分开,很多“为什么这个工具里要多写反斜线”的问题就会顺下来。
