深色模式
正则表达式是什么
概述
正则表达式不是某一种唯一固定的语法,它更像一类“用模式描述字符串”的方法。平时说“会正则”,真正的意思通常不是背下多少元字符,而是知道自己面对的是哪一套规则。
这件事之所以容易讲乱,是因为历史上确实存在几条不同的演化路线。Unix 工具里的 grep、sed、awk,编程语言里的 JavaScript、Python、Go、Java、Rust,看起来都在写正则,实际却不完全是一回事。
正则最初是拿来做什么的
正则表达式要解决的问题很朴素:用一段紧凑的规则,描述“一类字符串”。
比如下面三个需求,都适合用正则表达式来描述:
- 找出所有以
error开头的日志行 - 判断输入是不是邮箱地址那种固定格式
- 从一段文本里提取日期、端口号、版本号
如果没有正则,只能把这些判断拆成很多字符串操作。正则的价值就在于,它把“匹配规则”单独提炼成了一种可复用的表达方式。
它的来历并不神秘
“正则”这个名字,最早来自形式语言和自动机理论。1950 年代,Stephen Cole Kleene 用 regular set 这类概念描述可识别的字符串集合,这也是 regular expression 这个名字的来源。
后来 Ken Thompson 把这套想法带进了 Unix 世界,先后出现在 QED、ed、grep 这样的工具里。再往后,POSIX 对常见 Unix 工具中的正则行为做了规范化,才有了后来经常看到的 BRE 和 ERE。
所以正则一开始就不是为了 Web 表单校验发明的,它先是理论概念,后来才变成文本处理工具的日常能力。
为什么会有这么多“像正则又不完全一样”的东西
关键原因有两个:
- 先有实现,后有统一规范,不同工具的历史包袱很多
- 后来的编程语言为了更强的表达能力,又在 POSIX 之外继续扩展
结果就是,今天常见的“正则”至少可以分成下面几类:
| 类型 | 代表场景 | 重点 |
|---|---|---|
| POSIX BRE | grep、传统 sed 默认模式 | 转义多,偏老派 |
| POSIX ERE | grep -E、awk 常见模式 | 比 BRE 更顺手 |
| Perl 风格正则 | perl、php、很多现代语言 | 功能多,特性最丰富 |
| RE2 风格实现 | Go、Rust 一类强调线性时间的实现 | 放弃部分高级特性 |
Shell glob | 文件名匹配、通配符展开 | 不是正则,只是长得像 |
这里最容易混淆的是最后一类。*、?、[] 在 glob 里也存在,但它们服务的是路径名展开,不是正则语法。
先分清规范、实现、方言
读正则资料时,最好先问三个问题:
- 这是哪套规范,还是某个工具自己的实现。
- 这是命令行工具里的正则,还是编程语言里的正则。
- 这段写法是不是根本就不是正则,而是
glob或别的模式语法。
只要这三个问题没分开,后面的很多争论都会变成鸡同鸭讲。比如有人说“正则支持环视”,有人说“不支持”,这两句话都可能是对的,只是说的不是同一套东西。
这组文章接下来怎么展开
后面的内容按这个顺序展开:
- 先讲所有流派里都常见的基础匹配思路
- 再讲 POSIX 里的
BRE和ERE - 然后把命令行工具和 Shell 的边界讲清楚
- 最后再看常见编程语言各自站在哪一派
这样读下来,正则就不会再是一堆零散符号,而是一张比较清楚的地图。
