速查手册: 正则表达式

正则表达式(Regular Expression)是一种用于描述字符模式的语言。核心用途是文本的搜索、匹配和替换。

1. 核心元字符

1.1. 字符匹配

符号说明示例
.匹配除换行符外的任意单个字符a.c 匹配 abc
[]匹配方括号内的任意单个字符[abc] 匹配 a
[^]匹配不在方括号内的任意单个字符[^abc] 匹配 d
\d匹配一个数字,等价于 [0-9]\d 匹配 5
\D匹配一个非数字,等价于 [^0-9]\D 匹配 a
\w匹配字母、数字、下划线,等价于 [a-zA-Z0-9_]\w 匹配 b
\W匹配非字母、数字、下划线\W 匹配 *
\s匹配任意空白符(空格、制表符、换行符等)\s 匹配
\S匹配任意非空白符\S 匹配 x

1.2. 量词

符号说明
*匹配前一个元素 0 次或多次
+匹配前一个元素 1 次或多次
?匹配前一个元素 0 次或 1 次
{n}匹配前一个元素恰好 n 次
{n,}匹配前一个元素至少 n 次
{n,m}匹配前一个元素 n 到 m 次

1.3. 边界与定位

符号说明
^匹配字符串的开头
$匹配字符串的结尾
\b匹配单词边界(单词字符与非单词字符之间)
\B匹配非单词边界

1.4. 分组与分支

符号说明
()分组,将多个元素视为一个整体,并捕获内容
|或,匹配 | 左边或右边的表达式

2. 量词模式

2.1. 贪婪模式(Greedy)

默认模式。尽可能多地匹配。

  • 表达式: a.*b
  • 文本: axbyazb
  • 匹配结果: axbyazb

2.2. 懒惰模式(Lazy)

在量词后加 ?。尽可能少地匹配。

  • 表达式: a.*?b
  • 文本: axbyazb
  • 匹配结果: axb

2.3. 独占模式(Possessive)

在量词后加 +(部分引擎支持,如 Java, PCRE)。尽可能多地匹配,且不回溯。用于性能优化。

  • 表达式: a.*+b
  • 文本: axbyazb
  • 匹配结果: 匹配失败(因为 .*+ 吃掉了所有字符直到结尾,不会为了匹配 b 而吐出字符)。

3. 高级构造

3.1. 非捕获组 (?:...)

功能与 () 相同,但不创建捕获组。在只需要分组功能而不需要引用其内容时使用,性能略好。

  • (\d{4})-(\d{2}) 捕获 202406
  • (?:\d{4})-(\d{2}) 只捕获 06

3.2. 环视(Lookaround)

只匹配位置,不消耗字符。

  • 正向先行断言 (?=...)
    • 说明:匹配一个位置,该位置后面能匹配 ...
    • 示例:Windows(?= 7| 8| 10) 匹配 Windows 7 中的 Windows
  • 负向先行断言 (?!...)
    • 说明:匹配一个位置,该位置后面不能匹配 ...
    • 示例:\d{3}(?!px) 匹配 123em 中的 123,但不匹配 456px 中的 456
  • 正向后行断言 (?<=...)
    • 说明:匹配一个位置,该位置前面能匹配 ...
    • 示例:(?<=\$)\d+ 匹配 $100 中的 100
  • 负向后行断言 (?<!...)
    • 说明:匹配一个位置,该位置前面不能匹配 ...
    • 示例:(?<!http:)\/\/\w+ 匹配 ftp://example.com 中的 //example.com

4. 性能与陷阱

4.1. 核心问题:回溯(Backtracking)

当一个模式有多种匹配方式时(如 (a|b)*),正则引擎会尝试所有可能路径,直到找到匹配或穷尽所有可能。这个过程叫回溯。

4.2. 灾难性回溯(Catastrophic Backtracking)

当一个模糊的、嵌套的量词模式遇到不匹配的長字符串时,回溯次数会呈指数级增长,导致 CPU 占用 100%,程序卡死。

  • 典型陷阱^(a+)+$
  • 触发文本aaaaaaaaaaaaaaaaaaaaaaaa!
  • 原因:引擎为了匹配结尾的 $,会尝试 (a+) 的所有可能的分组组合,数量巨大。

4.3. 避免方法

  1. 使模式更具体:避免在嵌套量词中使用模糊的表达式。例如,用 ^a+(a+)*$ 改写可能稍好,但根源问题仍在。
  2. 使用独占模式或原子组^(a++)+$^(?>a+)+$。它们匹配后不回溯,直接切断了指数级增长的可能。
  3. 减少模糊性:明确知道要匹配什么,而不是让引擎去猜。

5. 实践建议

  1. 使用工具:使用 Regex101 等在线工具进行测试和调试。它能可视化匹配过程和回溯步骤。
  2. 避免造轮子:对于校验邮箱、URL 等符合 RFC 标准的复杂字符串,直接使用经过验证的库。手写的简单正则基本无法覆盖所有边界情况。
  3. 优先可读性:复杂的正则难以维护。必要时,宁愿拆分成多个简单的正则,或用代码逻辑辅助。


已发布

分类

来自

标签:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注