正则表达式是一种强大的文本模式匹配工具,理解它确实需要系统性地梳理。下面我从基础到进阶,为你详细介绍正则表达式。

正则表达式(Regular Expression,简称 regex 或 regexp)是一个描述字符模式的表达式。你可以把它理解为一个高度专业化的“搜索词”,用来在文本中查找、匹配、替换符合特定规则的字符串。
正则表达式的核心由字面字符和元字符组成。字面字符就是它自身,比如 a 匹配字母 a;元字符则具有特殊含义。
| 元字符 | 含义 | 示例 |
|---|---|---|
. | 匹配任意单个字符(除换行符外) | a.c 匹配 "abc"、"aac"、"a c" |
^ | 匹配行的开始 | ^Hello 匹配行首的 "Hello" |
$ | 匹配行的结束 | end$ 匹配行尾的 "end" |
* | 匹配前面的元素零次或多次 | ab* 匹配 "a"、"ab"、"abb" |
+ | 匹配前面的元素一次或多次 | ab+ 匹配 "ab"、"abb",不匹配 "a" |
? | 匹配前面的元素零次或一次 | ab? 匹配 "a" 或 "ab" |
| | 逻辑或 | cat|dog 匹配 "cat" 或 "dog" |
() | 将内部的表达式视为一个整体 | (ab)+ 匹配 "ab"、"abab" |
[] | 字符类,匹配其中任意一个字符 | [abc] 匹配 "a"、"b"、"c" |
[^] | 否定字符类,匹配不在其中的任意一个字符 | [^abc] 匹配除 "a"、"b"、"c" 外的任意字符 |
\ | 转义,将元字符转为普通字符 | \. 匹配字面点号 |
反斜杠不仅可以转义字符,还可以简化某些常见的单字符匹配:
| 简写 | 含义 | 等价于 |
|---|---|---|
\d | 任意数字 | [0-9] |
\D | 非数字 | [^0-9] |
\w | 单词字符(字母、数字、下划线) | [a-zA-Z0-9_] |
\W | 非单词字符 | [^a-zA-Z0-9_] |
\s | 空白字符(空格、制表符、换行等) | [ \t\n\r\f\v] |
\S | 非空白字符 | [^ \t\n\r\f\v] |
前面给出的功能足以让我们匹配一种特定的字符串或一类特定的单个字符,但如果我们想要重复这样的匹配呢?Bingo!量词就是为解决这类问题而生的。
量词能够精确控制一种匹配模式重复的次数。
| 量词 | 含义 |
|---|---|
{n} | 精确匹配 n 次 |
{n,} | 至少 n 次 |
{n,m} | 至少 n 次,至多 m 次 |
* | 等价于 {0,} |
+ | 等价于 {1,} |
? | 等价于 {0,1} |
默认情况下,量词是贪婪的——它会尽可能多地匹配。
.* 会匹配尽可能长的字符串.*? 会匹配尽可能短的字符串示例:对于文本 <div>content</div> <div>more</div>
/<.*>/ 匹配整个 <div>content</div> <div>more</div>/<.*?>/ 分别匹配 <div>、</div> 等单个标签零宽断言匹配的是位置而不是字符,它不消耗文本。
| 断言 | 名称 | 含义 |
|---|---|---|
(?=...) | 正向先行断言 | 右侧必须是... |
(?!...) | 负向先行断言 | 右侧不能是... |
(?<=...) | 正向后行断言 | 左侧必须是... |
(?<!...) | 负向后行断言 | 左侧不能是... |
示例:
\d+(?=元) 匹配后面跟着"元"的数字,如匹配"100元"中的"100",但不匹配"100美元"(?<=\$)\d+ 匹配前面有美元符号的数字,如匹配"$100"中的"100"(pattern):捕获分组,匹配的内容可以被提取或反向引用(?:pattern):非捕获分组,仅用于分组但不保存匹配内容用 \1、\2 等引用之前捕获分组的内容。
示例:<(\w+)>.*?</\1> 匹配成对的 HTML 标签,如 <div>content</div>
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
^1[3-9]\d{9}$
https?://([^/]+)
\b(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})\.(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})\.(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})\.(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})\b
\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])
正则表达式在不同编程语言中基本语法相同,但细节有差异:
| 特性 | Python | JavaScript | Java | PHP |
|---|---|---|---|---|
| 后行断言 | ✅ (3.8+) | ✅ (ES2018+) | ✅ | ✅ |
| 标志 | re.IGNORECASE | /i | Pattern.CASE_INSENSITIVE | /i |
| 匹配方法 | re.match / re.search | test / exec | Matcher.find | preg_match |
常用标志:
i:忽略大小写g:全局匹配(查找所有,而非第一个)m:多行模式(^ $ 匹配每行的开始/结束)s:点号匹配换行符startsWith、split),直接使用语言内置方法更清晰高效(a+)+)可能导致灾难性回溯,应避免或改写x 标志(忽略空白)添加注释,或拆分为多个步骤\\d| 功能 | 表达式 |
|---|---|
| 数字 | \d |
| 非数字 | \D |
| 字母数字 | \w |
| 空白 | \s |
| 任意字符 | . |
| 行首 | ^ |
| 行尾 | $ |
| 单词边界 | \b |
| 或 | | |
| 分组 | () |
| 任意一个 | [] |
| 排除 | [^] |
| 零次或多次 | * |
| 一次或多次 | + |
| 零次或一次 | ? |
| 精确 n 次 | {n} |
| 至少 n 次 | {n,} |
| n 到 m 次 | {n,m} |
你的 Rust 标准库并未提供任何关于正则表达式的支持,但是社区生态弥补了这一点。这次我们简要介绍 regex crate 的使用。
在该库中,所有正则表达式都是一个类型为 Regex 的值;为了构建正则表达式,我们需要一个字符串(称为“模式”):
use regex::Regex;
let pat = r"Homer (.)\. Simpson"; // 构建所需的模式
let re = Regex::new(pat).unwrap(); // 用模式构建正则表达式
现在我们可以使用它来