服务器设计 之 【正则表达式及C++正则库的简介与使用】

目录

1.正则表达式

1.2.基础元字符:字符类的原子

1.3.字符集合与范围:自定义字符类

1.4.量词:重复次数控制

1.5.位置锚定:边界断言

1.6.分组与捕获

1.7.逻辑选择与修饰符

[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.4.std::regex_match

[2.5. std::regex_search](#2.5. std::regex_search)

2.6.std::regex_replace

2.7.std::regex_iterator

2.8.std::regex_token_iterator

2.9.标志

2.10.使用细节简介


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] 匹配 abc任意一个
[^abc] 匹配 abc 之外的任意一个字符(脱字符在开头表取反)
[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]+ 匹配一个或多个由 ab 组成的序列 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_referencesub_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
  • 在目标字符串中查找第一个与正则表达式匹配的子串
接口 说明
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_bolmatch_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)?`
相关推荐
RNEA ESIO21 小时前
SQL中的REGEXP正则表达式使用指南
数据库·sql·正则表达式
研☆香3 天前
聊一聊js中的正则表达式的应用
前端·javascript·正则表达式
麦芽糖02194 天前
python进阶六 正则表达式
android·python·正则表达式
Watermelo6176 天前
理解 JavaScript 中的“ / ”:路径、资源与目录、nginx配置、请求、转义的那些事
前端·javascript·vue.js·chrome·nginx·正则表达式·seo
睡不着的可乐6 天前
正则表达式
正则表达式
Dxy12393102166 天前
Python 使用正则表达式将多个空格替换为一个空格
开发语言·python·正则表达式
2401_827499999 天前
python项目实战11-正则表达式基础
python·mysql·正则表达式
互联网散修9 天前
零基础鸿蒙应用开发第三十三节:正则表达式基础与应用
华为·正则表达式·harmonyos
bukeyiwanshui9 天前
20260414 正则表达式及shell三剑客
数据库·mysql·正则表达式