深色模式
编程语言里的正则流派
概述
到了编程语言里,“正则”通常已经不再是 POSIX BRE 那个世界。很多语言的正则更接近 Perl 风格,语法更丰富,也更强调捕获、回溯、断言这类能力。
但“更接近 Perl 风格”也不等于“大家都一样”。有的语言为了性能或实现复杂度,会主动放弃一部分高级特性。真正有用的判断方式,不是背一句“某语言支持正则”,而是知道它大致属于哪一派。
可以先记住一个总原则
常见编程语言里的正则,大体可以分成两组:
- 更接近 Perl 风格,功能丰富,支持回溯型特性
- 更接近 RE2 这类强调线性时间的实现,故意砍掉部分高级能力
这也是为什么同样一段模式,在 JavaScript 或 Python 里能写,在 Go 或 Rust 里就未必能写。
几门常见语言的基本位置
| 语言 | 常见实现风格 | 大致特点 |
|---|---|---|
| JavaScript | ECMAScript 正则 | 现代前端最常见,功能比较完整 |
| Python | re 模块 | 和 Perl 风格接近,但不是 PCRE 原样照搬 |
| Java | java.util.regex | 功能丰富,工程里很常见 |
| PHP | PCRE 家族 | 和 Perl 风格关系很近 |
| Go | RE2 路线 | 强调线性时间,舍弃部分高级能力 |
| Rust | regex crate | 也偏 RE2 路线,重视性能和可预测性 |
这张表最重要的作用,不是记名词,而是建立预期。以后看到一段包含环视或反向引用的模式,就知道不能默认它在 Go 和 Rust 里也成立。
JavaScript 是很多人最熟的一套
JavaScript 使用的是 ECMAScript 自己定义的正则语法。现代环境里,常见能力包括:
- 分组和反向引用
- 非贪婪量词
- 选择和字符组
- 前瞻和后顾这类断言
- 命名分组
所以前端开发者经常接触到的“正则印象”,其实更接近现代语言正则,而不是 POSIX BRE 或 ERE。
这也是为什么很多人在 grep 里第一次发现 \d、(?=...) 不能随便用时,会有一种“怎么连正则都不会了”的错觉。不是不会,是换了语境。
Python 和 Java 也属于功能比较完整的一组
Python 的 re 模块、Java 的 java.util.regex,都属于现代开发里很常见的正则实现。它们通常支持:
- 分组和反向引用
- 非贪婪量词
- 前瞻和后顾
- 一批快捷字符组,例如
\d、\w、\s
但这里仍然不要偷懒地把它们直接等同于 PCRE。相似不等于相同,具体边角规则还是得看各自文档。
如果只做日常开发,这层差异未必天天碰到;但一旦写到复杂断言、Unicode、标志位组合,就最好别凭经验硬猜。
Go 和 Rust 走的是另一条路
Go 的标准库 regexp 和 Rust 常用的 regex crate,都更强调性能可预测性。为了避免某些模式带来的灾难性回溯,它们主动放弃了一部分高级能力。
最值得先记住的是:
- 通常不支持反向引用
- 通常不支持环视
- 支持基础分组、字符组、次数限定、选择
这不是“功能不完整”,而是设计取舍。它们更在意表达式在大文本上也能稳定工作,而不是尽量兼容 Perl 风格的全部技巧。
如果项目在 Go 或 Rust 里,最稳妥的策略往往不是“想办法把 Perl 风格写法硬搬过去”,而是先确认这类高级需求是否真的值得用正则承担。
PHP 往往最接近大家口中的 PCRE
PHP 项目里常见的是 PCRE 家族实现,所以不少“网上搜到的高级正则技巧”,在 PHP 里跑通的概率会比较高。
这也解释了一个常见现象:同样是后端开发,PHP 工程师和 Go 工程师对“正则到底能写多复杂”这件事,直觉往往完全不一样。不是谁记错了,而是底层引擎路线不同。
一张够用的判断表
下面这张表只保留最常用的判断项:
| 语言 | 反向引用 | 环视 | 非贪婪量词 | \d / \w |
|---|---|---|---|---|
| JavaScript | 常见支持 | 常见支持 | 支持 | 支持 |
| Python | 常见支持 | 常见支持 | 支持 | 支持 |
| Java | 常见支持 | 常见支持 | 支持 | 支持 |
| PHP | 常见支持 | 常见支持 | 支持 | 支持 |
| Go | 不支持 | 不支持 | 支持 | 支持 |
| Rust | 不支持 | 不支持 | 支持 | 支持 |
这张表不是语言规范全文,只是帮你在动手前先判断风险。越是复杂的模式,越不该靠“它看起来像正则所以应该能用”这种直觉。
实际工作里的判断方法
- 先看语言官方文档写的是哪套引擎或语法
- 看到环视、反向引用、命名分组时,优先确认支持范围
- 从命令行工具复制正则到代码里,或者反过来复制,都先重审一遍
- 一旦跨到 Go、Rust 这种实现,先怀疑高级特性是否可用
说到底,编程语言里的正则不是一门统一方言,而是一群长得像亲戚的方言。知道它们的血缘关系,比背更多花哨写法更有用。
