目录
[2.C++ 的正则表达式库 简介](#2.C++ 的正则表达式库 简介)
[2.1.std::basic_regex 及其别名](#2.1.std::basic_regex 及其别名)
[2.2.std::match_results 及其别名](#2.2.std::match_results 及其别名)
[2.3.std::sub_match 及其别名](#2.3.std::sub_match 及其别名)
[2.5. std::regex_search](#2.5. std::regex_search)
1.正则表达式
- 正则表达式(Regular Expression,简称 regex 或 regexp)是一套用于描述、匹配和操作字符串的规则语言
- 它通过特定的字符组合定义搜索模式,应用于文本处理、数据验证、日志分析等场景中
1.2.基础元字符:字符类的原子
正则中,一些特殊符号代表某一类字符
| 元字符 | 含义 | 等价表达式 |
|---|---|---|
. |
匹配除换行符外的任意单个字符 | [^\n\r] |
\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] |
\t |
制表符(Tab) | --- |
\n |
换行符 | --- |
\r |
回车符 | --- |
- c.t 匹配 cat、cbt、c t,不匹配 ct 或 caat
- \d{3}-\d{4} 匹配美国邮编格式如 123-4567
1.3.字符集合与范围:自定义字符类
使用方括号 [] 自定义字符类,匹配的是一个字符(除非后面跟着量词如 +、*、{n})
| 写法 | 说明 |
|---|---|
[abc] |
匹配 a、b、c 中任意一个 |
[^abc] |
匹配除 a、b、c 之外的任意一个字符(脱字符在开头表取反) |
[a-z] |
匹配一个小写字母 a 到 z |
[0-9A-F] |
匹配一个十六进制数字字符 |
[a-zA-Z] |
匹配一个任意大小写字母 |
在正则表达式的方括号 [] 内部,除了**开头的 ^(取反)、中间的 -(基于 ASCII/Unicode 码点的范围,必须放在两个字符之间)、结束的 ] 以及转义符 \**外,其他所有元字符(如 . * + ? | 等)都只代表它们自己这个普通字符
| 字符 | 特殊含义的条件 | 作为普通字符的条件 |
|---|---|---|
^ |
紧跟在 [ 后面(取反) |
不在第一个位置(例如 [a^]) |
- |
在两个字符之间 (表示范围,如 a-z) |
在开头 [-abc] 或结尾 [abc-] |
] |
结束字符类 | 必须转义 \](几乎无法作为普通字符放在中间而不转义) |
- 在自定义字符类内部使用预定义字符类
| 写法 | 含义 |
|---|---|
[\d] |
等价于 [0-9] |
[\w] |
等价于 [A-Za-z0-9_] |
[\s] |
匹配任何一个空白字符(空格、制表符等) |
[\d\s] |
匹配一个数字或空白字符 |
在字符类内部,\d、\w、\s 仍然有效,但它们的取反版本(\D、\W、\S)在字符类内没有特殊含义,会变成匹配字面量 D、W、S
大多数正则引擎中,[] 会报错
1.4.量词:重复次数控制
量词只作用于它紧邻的一个原子:
- 字符:a+ 匹配一个或多个 a
- 字符类:[ab]+ 匹配一个或多个(任意组合的 a 或 b)
- 分组:(abc)+ 匹配一个或多个 abc(如 abcabcabc)
- 注意:ab+ 匹配的是 a 后面跟一个或多个 b,而不是 ab 重复。要重复 ab 必须用 (ab)+
| 量词 | 含义 | 匹配次数 |
|---|---|---|
? |
零次或一次(可选) | {0,1} |
* |
零次或多次(任意次) | {0,} |
+ |
一次或多次(至少一次) | {1,} |
{n} |
恰好 n 次 | {n} |
{n,} |
至少 n 次 | {n,} |
{n,m} |
至少 n 次,最多 m 次 | {n,m} |
- .* 不匹配换行
- 贪婪(默认 * +):在保证整个表达式还能匹配成功的前提下,尽可能多地重复
- 懒惰(加 ?):在保证整个表达式还能匹配成功的前提下,尽可能少地重复
| 正则 | 字符串 | 匹配结果(贪婪) | 匹配结果(懒惰,加?) |
|---|---|---|---|
a.*b |
aababb |
aababb(整串) |
aab,然后 ab |
<.*> |
<div>text</div> |
<div>text</div>(整串) |
<div>,然后 </div> |
".*" |
"one" "two" |
"one" "two"(整串) |
"one",然后 "two" |
1.5.位置锚定:边界断言
不消耗字符,只匹配位置
(1)基础锚点:^ 和 $
| 锚点 | 含义 | 注意 |
|---|---|---|
^ |
匹配字符串开头(或多行模式下的行首) | 在字符类 [^] 中表示"取反",不同语境 |
$ |
匹配字符串结尾(或多行模式下的行尾) | 注意 $ 前如果有 \n 也可以匹配(引擎相关) |
文本:"Hello world"(单行)
- ^Hello 匹配 Hello(在开头)
- world$ 匹配 world(在结尾)
- ^world 不匹配
(多行)
Hello
world
- /^world/m 匹配第二行的 world(常见的定界符就是正斜杠 /,成对出现)
- 默认无 m 时 ^ 只匹配整个字符串的最开头
(2)单词边界:\b 和 \B
\w 组成单词字符:[A-Za-z0-9_](默认 ASCII 模式,Unicode 模式更广)
| 锚点 | 匹配位置 | 本质 |
|---|---|---|
\b |
单词边界 | \w 和 \W(或开头/结尾)之间的位置 |
\B |
非单词边界 | 不是 \b 的位置(\w 内部或 \W 内部) |
- \b = 单独(作为独立单词)
- \B = 不单独(作为单词的一部分)
| 正则 | 含义 | 通俗说法 |
|---|---|---|
\bcat\b |
独立的 cat |
"就它自己,前后不是单词字符" |
\Bcat\B |
夹在单词内部的 cat |
"前后都连着其他单词字符" |
\bcat |
开头的 cat |
"前面是边界(开头或非单词字符),后面无所谓" |
cat\b |
结尾的 cat |
"后面是边界,前面无所谓" |
1.6.分组与捕获
(1)普通捕获组 (...)
**分组:**将多个字符/元字符组合成一个单元,让量词(*、+、{n} 等)作用于整个组
**捕获:**记住这个组匹配到的具体文本,编号从 1 开始
(\d{4})-(\d{2})-(\d{2})
- 匹配日期 2026-04-22,捕获组 1 为 2026,组 2 为 04,组 3 为 22
(2)非捕获组 (?:...)
只分组,不捕获 → 不占用编号,性能稍好,正则更清晰
(?:http|ftp)://([^/\r\n]+)
- 匹配 http://example.com,捕获的只是域名部分
- (?:http|ftp) 作为一个整体,匹配 http 或 ftp
- 捕获组 1 = 域名
- 如果不加 ?::(http|ftp)://([^/\r\n]+) → 协议会占组1,域名占组2,多了一层
(3)反向引用 \n
匹配之前捕获组匹配到的相同内容(不是相同的模式,而是相同的字符串)
文本:"<div>内容</div>"
正则:<(\w+)>.*?</\1>
- \1 引用的组1的内容 div
- 能匹配 <div>...</div>,但不会匹配 <div>...</span>
文本:"the the cat"
正则:\b(\w+)\s+\1\b
- 匹配 the the
文本:Hello 'world' and \"python\""
正则:(['"]).*?\1
- 先匹配 ' 或 ",\1 保证闭合的是同一种引号
- 第一次匹配结果:'world'
- 第二次匹配结果:"python"
(4)反向引用的编号
- 编号按捕获组出现的顺序(左括号的位置),非捕获组 (?:...) 不计入
- \0 一般表示整个匹配(部分语言支持)
- 超过 9:\10 表示第 10 个组(注意:\10 可能被解析为八进制转义,建议用 \g{10} 或 ${10})
1.7.逻辑选择与修饰符
(1)或运算符 |
作用:在多个分支中选择一个匹配
| 的优先级低于连接、量词、锚点等几乎所有操作符
# 文本 "foot"
(foot|foo) → 匹配 "foot"(先试 foot 成功)
(foo|foot) → 匹配 "foo"(先试 foo 成功,不会再试 foot)
- 在大多数引擎中,| 是短路的:从左到右尝试,第一个匹配成功的分支就停止
- 这可能导致意外,如果长的分支放在后面,可能永远不会被匹配到
(2)修饰符(标志)
加在正则字面量末尾或作为函数参数,影响匹配行为
| 修饰符 | 作用 |
|---|---|
i |
忽略大小写 |
g |
全局匹配(找到所有匹配,而不止第一个)(默认匹配第一个就停止) |
m |
多行模式:^ 和 $ 匹配行首行尾,而非仅字符串首尾 |
s |
点号匹配所有字符模式:. 也匹配换行符 |
u |
启用 Unicode 完整支持 |
y |
粘滞模式(sticky),从 lastIndex 位置严格匹配 |
- m - 多行模式
作用:改变 ^ 和 $ 的行为
| 模式 | ^ 匹配 |
$ 匹配 |
|---|---|---|
无 m |
仅字符串开头 | 仅字符串结尾(或结尾的 \n 前) |
有 m |
字符串开头 + 每个 \n 后面 |
字符串结尾 + 每个 \n 前面 |
文本: "line1\nline2\nline3"
/^line/g → 匹配第一个 "line"(无 m)
/^line/gm → 匹配三个 "line"(每行开头)
/line$/gm → 匹配每行结尾的 "line"
^ 和 $ 仍然不消耗 \n,只是位置锚定变了
1.8.运算符优先级
| 优先级 | 运算符/元素 | 说明 | 正面例子 | 反面例子(容易误解) |
|---|---|---|---|---|
| 1 | 转义符 \ |
改变紧随其后的字符的字面意义 | \. 匹配字面量句号 |
\d 中的 \ |
| 2 | 字符类/分组 [...] [^...] (...) (?:...) |
方括号和圆括号构造的单元 | [a-z] 是一个整体 |
ab[cd] 是 a + b + [cd] |
| 3 | 量词 * + ? {n,m} |
紧贴在它前面的一个原子(字符、字符类或分组)上 | a+ 匹配一个或多个 a;[ab]+ 匹配一个或多个由 a 或 b 组成的序列 |
ab+ 匹配 a + b+,而不是 (ab)+ |
| 4 | 连接(拼接) | 两个相邻的原子,中间没有操作符 | ab 匹配 a 后紧跟 b |
空格在大部分模式里就是一个普通字符 |
| 5 | 锚点 ^ $ \b \B |
匹配位置,不消耗字符 | ^a 匹配行首的 a |
a^ 是无效的(位置锚点放中间没意义) |
| 5 | 或 ` | ` | 在多个分支之间选择 | `cat |
2.C++ 的正则表达式库 <regex> 简介
2.1.std::basic_regex 及其别名
- **该类表示一个正则表达式对象,**是后续所有匹配操作的基础
- 常用别名有 std::regex (用于 std::string) 和 std::wregex (用于 std::wstring)
| 接口 | 说明 |
|---|---|
regex() |
默认构造函数,创建一个空的正则表达式 |
regex(const charT* s, flag_type f = ECMAScript) |
从C风格字符串构造 |
regex(const stringT& s, flag_type f = ECMAScript) |
从 std::string 构造 |
regex& operator=(const regex& other) |
赋值操作符 |
regex& assign(const charT* s, flag_type f = ECMAScript) |
将一个正则表达式赋值给对象 |
unsigned mark_count() const |
返回表达式中捕获组的数量 |
flag_type flags() const |
返回构造时使用的语法标志 |
2.2.std::match_results 及其别名
- 该类存储正则匹配的结果,通过 smatch 对象可以访问所有匹配信息,包括子匹配(捕获组)
- 常用别名:std::smatch (用于 std::string) 和 std::cmatch (用于 const char*)
- 存储了**完整的匹配信息:**整个匹配的字符串(索引 0),所有捕获组的内容(索引 1, 2, 3...)
| 接口 | 说明 |
|---|---|
bool empty() const |
检查是否有匹配结果 |
size_t size() const |
返回子匹配(捕获组)的数量,包括整个匹配 |
size_t max_size() const |
返回可存储的最大子匹配数量 |
const_reference operator[](size_t n) const |
返回第 n 个子匹配(const_reference 为 sub_match) |
const_reference prefix() const |
返回目标序列中匹配项之前的子序列 |
const_reference suffix() const |
返回目标序列中匹配项之后的子序列 |
string_type format(const charT* fmt, ...) const |
根据格式字符串和匹配结果生成新字符串 |
2.3.std::sub_match 及其别名
- sub_match 对象由 match_results 的 operator[] 返回,表示一个具体的捕获组
| 接口 | 说明 |
|---|---|
bool matched |
布尔成员,指示此子匹配是否成功 |
difference_type length() const |
返回子匹配的长度 |
string_type str() const |
将子匹配转换为字符串 |
operator string_type() const |
隐式转换为字符串 |
int compare(const sub_match& s) const |
与另一个 sub_match 对象比较 |
2.4.std::regex_match
- 用于判断整个目标字符串是否与正则表达式完全匹配
| 接口 | 说明 |
|---|---|
bool regex_match(BidirIt first, BidirIt last, match_results& m, const regex& e, match_flag_type flags = match_default) |
在迭代器范围 [first, last) 内进行匹配,结果存入 m |
bool regex_match(BidirIt first, BidirIt last, const regex& e, match_flag_type flags = match_default) |
在迭代器范围 [first, last) 内进行匹配,忽略结果 |
bool regex_match(const charT* str, match_results& m, const regex& e, match_flag_type flags = match_default) |
在C风格字符串上匹配,结果存入 m |
bool regex_match(const charT* str, const regex& e, match_flag_type flags = match_default) |
在C风格字符串上匹配,忽略结果 |
bool regex_match(const stringT& s, match_results& m, const regex& e, match_flag_type flags = match_default) |
在 std::string 上匹配,结果存入 m |
bool regex_match(const stringT& s, const regex& e, match_flag_type flags = match_default) |
在 std::string 上匹配,忽略结果 |
bool regex_match(const charT* str, match_results& m, const regex& e, match_flag_type flags = match_default) |
(对于数组) 在C风格字符串上匹配,结果存入 m |
2.5. std::regex_search
- 在目标字符串中查找第一个与正则表达式匹配的子串
| 接口 | 说明 |
|---|---|
bool regex_search(BidirIt first, BidirIt last, match_results& m, const regex& e, match_flag_type flags = match_default) |
在迭代器范围 [first, last) 内搜索,结果存入 m |
bool regex_search(BidirIt first, BidirIt last, const regex& e, match_flag_type flags = match_default) |
在迭代器范围 [first, last) 内搜索,忽略结果 |
bool regex_search(const charT* str, match_results& m, const regex& e, match_flag_type flags = match_default) |
在C风格字符串上搜索,结果存入 m |
bool regex_search(const charT* str, const regex& e, match_flag_type flags = match_default) |
在C风格字符串上搜索,忽略结果 |
bool regex_search(const stringT& s, match_results& m, const regex& e, match_flag_type flags = match_default) |
在 std::string 上搜索,结果存入 m |
bool regex_search(const stringT& s, const regex& e, match_flag_type flags = match_default) |
在 std::string 上搜索,忽略结果 |
2.6.std::regex_replace
- 将目标字符串中所有匹配的部分替换为指定格式的字符串
| 接口 | 说明 |
|---|---|
OutIt regex_replace(OutIt out, BidirIt first, BidirIt last, const regex& e, const stringT& fmt, match_flag_type flags = match_default) |
将迭代器范围 [first, last) 内的替换结果写入输出迭代器 out |
stringT regex_replace(const stringT& s, const regex& e, const stringT& fmt, match_flag_type flags = match_default) |
返回一个替换后的新字符串 |
stringT regex_replace(const charT* s, const regex& e, const stringT& fmt, match_flag_type flags = match_default) |
返回一个替换后的新字符串 |
2.7.std::regex_iterator
- 用于遍历一个字符串中的所有匹配项。常用别名有 std::sregex_iterator (用于 std::string)
| 接口 | 说明 |
|---|---|
regex_iterator() |
默认构造函数,构造一个尾后迭代器 |
regex_iterator(BidirIt first, BidirIt last, const regex& e, match_flag_type flags = match_default) |
在范围 [first, last) 内进行迭代 |
bool operator==(const regex_iterator& rhs) const |
比较两个迭代器是否相等 |
const value_type& operator*() const |
返回当前匹配的 match_results 对象 |
regex_iterator& operator++() |
移动到下一个匹配 |
2.8.std::regex_token_iterator
- 用于遍历一个字符串中所有匹配的特定子表达式(如特定捕获组),常用于字符串分割
| 接口 | 说明 |
|---|---|
regex_token_iterator() |
默认构造函数,构造一个尾后迭代器 |
regex_token_iterator(BidirIt first, BidirIt last, const regex& e, int submatch = 0, match_flag_type flags = match_default) |
迭代第 submatch 个捕获组 |
bool operator==(const regex_token_iterator& rhs) const |
比较两个迭代器是否相等 |
const value_type& operator*() const |
返回当前子匹配的 sub_match 对象 |
regex_token_iterator& operator++() |
移动到下一个匹配 |
2.9.标志
(1)语法标志 (用于构造 std::regex)
- 定义在 std::regex_constants 命名空间中,决定正则表达式的语法风格。默认使用 ECMAScript
| 标志 | 说明 |
|---|---|
ECMAScript |
默认。使用改进的 ECMAScript 正则表达式语法。 |
basic |
使用 POSIX 基本正则表达式语法。 |
extended |
使用 POSIX 扩展正则表达式语法。 |
awk |
使用 POSIX awk 工具的正则表达式语法。 |
grep |
使用 POSIX grep 工具的正则表达式语法,基于 basic 但允许 \n 分隔替代。 |
egrep |
使用 POSIX grep -E 工具的正则表达式语法,基于 extended 但允许 \n 分隔替代。 |
icase |
匹配时忽略大小写。 |
nosubs |
不存储任何子匹配(捕获组),可提升性能。 |
optimize |
指示引擎进行优化,可能加快匹配速度,但会增加构造时间。 |
collate |
让形如 [a-b] 的字符范围对当前区域设置(locale)敏感。 |
multiline |
(C++17) 在多行模式下,^ 和 $ 匹配行的开头和结尾,而非整个字符串的开头和结尾。 |
(2)匹配标志 (用于 regex_match, regex_search, regex_replace)
- 定义在 std::regex_constants 命名空间中,用于调整匹配过程的行为
| 标志 | 说明 |
|---|---|
match_default |
默认行为,为空掩码。 |
match_not_bol |
将第一个字符视为不在行首,即 ^ 不会匹配第一个字符。 |
match_not_eol |
将最后一个字符视为不在行尾,即 $ 不会匹配最后一个字符。 |
match_not_bow |
将第一个字符视为不在单词边界,即 \b 不会匹配第一个字符。 |
match_not_eow |
将最后一个字符视为不在单词边界,即 \b 不会匹配最后一个字符。 |
match_any |
如果存在多个匹配,任何匹配都是可接受的(不保证最长或最早)。 |
match_not_null |
不匹配空序列。 |
match_continuous |
仅匹配从 first 开始的子序列。 |
match_prev_avail |
指示 --first 是有效位置,会使 match_not_bol 和 match_not_bow 被忽略。 |
format_default |
在 regex_replace 中,使用 ECMAScript 的替换语法。 |
format_sed |
在 regex_replace 中,使用 POSIX sed 工具的替换语法。 |
format_no_copy |
在 regex_replace 中,不将未匹配的字符复制到输出。 |
format_first_only |
在 regex_replace 中,只替换第一个匹配项。 |
2.10.使用细节简介
(1)正则对象的构造开销极大
-
std::regex 的构造函数会执行正则解析 -> 语法树生成 -> 状态机编译,这是一个耗时的过程
-
错误做法 (在循环内重复编译),正确做法(单次编译,复用对象)
for (const auto& line : lines) {
std::regex re(R"(\d+)"); // 每次循环都重新编译,极慢
std::regex_search(line, re);
}static const std::regex re(R"(\d+)"); // 编译一次,重复使用
for (const auto& line : lines) {
std::regex_search(line, re);
}
(2)optimize 标志的误导性
- std::regex::optimize 标志指示库花更多时间构造正则对象以换取更快的匹配速度
- 适用场景:正则对象构造一次,但进行数百万次搜索(如服务器日志实时过滤)
- 反作用场景:正则对象频繁构造销毁,或者只使用一两次。此时开启 optimize 会显著增加构造时间,得不偿失
(3)利用 nosubs 进行微优化
如果只关心是否匹配(regex_match 或 regex_search 返回 bool),而不需要提取捕获组内容,务必使用 std::regex::nosubs 标志,这会通知引擎不要分配内存来存储子匹配结果
// 仅判断有效性,无需捕获邮箱前缀后缀
std::regex re(R"(\w+@\w+\.com)", std::regex::nosubs);
(4)匹配结果的生命周期
std::smatch m;
if (std::regex_search(str, m, re)) {
// m 存储的是迭代器/指针,指向 str 内部
// 如果 str 被销毁或修改,m 失效
}
- 需要保存结果时,用 m.str() 复制出来
(5)迭代器遍历所有匹配
auto begin = std::sregex_iterator(str.begin(), str.end(), re);
auto end = std::sregex_iterator();
for (auto it = begin; it != end; ++it) {
std::cout << it->str() << std::endl;
}
2.11.使用示例
#include <iostream>
#include <string>
#include <regex>
int main()
{
//http请求格式:GET /bitejiuyeke/login?user=xiaoming&123123 HTTP/1.1\r\n
std::string str = "GET /bitejiuyeke/login?user=xiaoming&123123 HTTP/1.1\n";
std::smatch matches;
std::regex e("(GET|DELETE|PUT) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\\n|\\r\\n)?");
//GET|HEAD|POST|PUT|DELETE 表示匹配并提取其中任意一个字符串
//[^?]* [^?]匹配非问号字符,后边的*表示次或多次
//\\?(.*) \\?表示原始的?字符(*)表示提取?之后的任意字符次或多次,直到遇到空格
//HTTP/1\.[01] 表示匹配以HTTP/1.开始,后边有个O或1的字符串
//(?:\\n|\\r\\n)? (:...)表示匹配某个格式字符串,但是不提取,最后的?表示的是匹配前边的表达式0次或1次
if(std::regex_match(str, matches, e))
for(auto& s : matches)
std::cout << s << std::endl;
else
std::cout << "匹配失败" << std::endl;
return 0;
}
| 部分 | 正则片段 | 匹配内容 |
|---|---|---|
| 方法 | `(GET | DELETE |
| 空格 | 分隔符 | |
| 路径+参数 | ([^?]*) |
捕获 ? 之前的所有内容 |
| 查询字符串(可选) | (?:\\?(.*))? |
非捕获组,内部捕获 ? 后的内容 |
| 空格 | 分隔符 | |
| HTTP 版本 | (HTTP/1\\.[01]) |
捕获 HTTP/1.0 或 HTTP/1.1 |
| 换行符(可选) | `(?:\n | \r\n)?` |