正则表达式
正则表达式是编程时常用的一种字符串模式匹配工具,C++ 、Python、JavaScript 等主流语言均提供了对应的实现。
它本质是一段描述匹配规则的字符串,组成规则的字符分为两类:具备特殊语法含义的元字符,和原样匹配的字面字符。基于正则可以实现文本格式校验、内容提取、查找替换等多种操作。
转义符 \
如果要匹配 . * + ? ^ $ [ ] ( ) 等本身具备特殊语法含义的元字符,需要在符号前加转义符 \,取消其特殊含义,匹配字符本身。
示例:
bash
正则表达式:1\+2=\?
测试字符串:what is the result: 1+2=?
字符集合(字符类)
使用 [] 定义一个字符集合,匹配集合中的任意一个字符。
-
普通枚举:
[abcde]匹配 a、b、c、d、e 中的任意一个字符bash正则表达式: h[abcde] 测试字符串: he has two dogs. -
范围表示:通过连接符
-表示连续字符范围,[a-e1-3]等价于[abcde123]bash正则表达式:[a-e1-3] 测试字符串:abcd1243 -
否定字符集:在
[后加^,表示匹配「不在集合内」的任意字符,如[^0-9]匹配所有非数字字符
字符集合简写
正则提供了常用字符集的快捷写法,书写更简洁:
-
\d:阿拉伯数字集合,等价于[0-9] -
\w:单词字符集合,等价于[a-zA-Z0-9_] -
\s:空白字符集合,等价于[\r\n\t\f\v] -
.:匹配换行符之外的任意单个字符
将上述简写改为大写形式,代表对原有集合取反:
-
\D:非数字字符,等价于[^0-9] -
\W:非单词字符 -
\S:非空白字符
重复匹配(量词)
用于指定前面的单个字符、字符集合或分组,可以重复出现的次数:
-
*:匹配 0 次或多次,比如dog*,表示do加上0~多个g,does,dog,dogg都会匹配成功 -
+:匹配 1 次或多次,比如#\\da-f+,表示至少一次出现数字或a到f的集合元素 -
?:匹配 0 次或 1 次,即标记该内容为可选 -
{n}:精确匹配 n 次 -
{n,}:匹配至少 n 次 -
{n,m}:匹配 n 到 m 次(包含两端边界)
捕获组与或运算
捕获组 (...)
用括号将一段规则包裹为一个整体,可配合量词控制整组的重复次数;同时会将组内匹配到的内容保存下来,后续可按序号提取对应内容,这个特性也叫「捕获」。
(dog)+表示dog至少连续出现一次,[dog]+表示文本中由dog中的任意字母连续组成的字符串。
(dog|cat)-\1表示捕获第一个分组,比如dog-dog或cat-cat
(\d{4})-(\d{2})-(\d{2})表示捕获三个分组,比如2025-12-20
非捕获组 (?:...)
仅将内容视为整体控制匹配,不保存匹配结果,无需提取内容时可使用以提升性能。
这是一个非捕获分组,不能获得分组匹配结果和引用
(\d{4})-(\d{2})-(?:\d{2})只会捕获前两个分组,不会捕获日期
或运算符 |
表示「或」逻辑,匹配符号左侧或右侧的规则,通常配合捕获组使用以限定或运算的范围。
示例:(c|g|p)ar 可匹配 car、gar、par
锚点
锚点不匹配具体字符,仅匹配字符串的位置,用于限定匹配的起止边界:
-
^:行首锚点,匹配字符串的起始位置,比如^he\w*\b,here it is 匹配成功,there is a dog匹配失败。 -
$:行尾锚点,匹配字符串的结束位置,比如pdf$,找到行尾的pdf后缀,doc1.pdf匹配成功
零宽度断言(前后预查)
属于非捕获组的一种,仅校验目标位置的前后是否符合规则,不消耗字符、不捕获内容,用于实现精准的条件匹配:
-
?=正先行断言:要求当前位置后面 必须匹配指定规则,比如\d+(?=\s*hours)找到后面有hours字符的数字,如13 hours -
?!负先行断言:要求当前位置后面 不能匹配指定规则,比如\d+(?!=\s*hours)找到后面没有hours字符的数字 -
?<=正后发断言:要求当前位置前面 必须匹配指定规则,比如(?<=http:\/\/).*$表示以http://开头的字符,如http:/<www.xxx.com> -
`? 负后发断言:要求当前位置前面不能匹配指定规则
匹配标志(修饰符)
用于修改正则整体的匹配规则,通常写在表达式外部:
-
i忽略大小写:匹配时不区分字母大小写 -
g全局匹配:返回所有匹配结果,而非仅返回第一个匹配项 -
m多行模式:让^和$匹配每一行的开头和结尾,而非整个字符串的首尾
贪婪匹配与惰性匹配
正则默认采用贪婪匹配模式:会尽可能匹配最长的符合规则的子串。
在量词后追加 ?,可切换为惰性匹配模式:会尽可能匹配最短的符合规则的子串。
示例:对字符串 The fat cat sat on the mat
-
贪婪模式
.*at会匹配整段字符串 -
惰性模式
.*?at仅匹配到第一个at就停止
在C++中的使用
在C++中使用正则表达式需要引入regex头文件,std::regex表示一个正则表达式,可以直接用正则表达式字符串来初始化。
cpp
regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
因为正则表达式中的转义符,反斜杠在C++中也是转义符,因此需要两个反斜杠表示正则表达式中的一个反斜杠。也可以使用原始字符串字面值的方式表示R"(字符串)",这种方式下转义字符不再起作用。
cpp
regex patten("(\\d{4})");
regex patten(R"((\d{4}))");
可以使用regex_match判断一个字符串是否完全匹配正则表达式,在C++正则表达式库中不支持开头和结尾符^和$。校验整个输入字符串是否完全符合正则规则 ,必须从头到尾 100% 匹配才算成功,差一个字符都返回 false。
参数与返回值
-
第 1 个参数:待匹配的输入字符串(这里是
line,即一行请求行文本) -
第 2 个参数:
smatch结果容器,用来输出匹配到的各组内容;如果只校验格式、不需要提取字段,可以不传这个参数 -
第 3 个参数:
std::regex正则对象,也就是编译好的匹配规则 -
返回值:
bool类型
-
true:整串完全匹配,结果已经写入smatch容器,可以提取捕获组 -
false:格式不匹配,smatch内容无意义
-
smatch 通过下标访问结果
-
subMatch[0]:整个正则表达式匹配到的完整字符串,也就是输入串里完全符合规则的全部内容。 -
subMatch[1]:第 1 个捕获组的内容,对应正则里第一个左括号(包裹的规则匹配到的内容。 -
subMatch[2]:第 2 个捕获组的内容,对应正则里第二个左括号包裹的规则。 -
以此类推,第 n 个下标对应第 n 个捕获组。
cpp
bool HttpRequest::ParseRequestLine_(const string& line) {
regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
smatch subMatch;
if(regex_match(line, subMatch, patten)) {
method_ = subMatch[1]; // 请求方法(GET/POST)
path_ = subMatch[2]; // 请求路径
version_ = subMatch[3]; // HTTP 版本
state_ = HEADERS; // 状态转换到 HEADERS
return true;
}
LOG_ERROR("RequestLine Error");
return false;
}
如果想要部分匹配,在字符串中查找第一个匹配值,可以使用regex_search,和regex_match类似,但只有匹配成功的捕获组会放入smatch中。
全局匹配迭代器 std::sregex_iterator
如果需要查找字符串中所有符合规则的匹配项 ,可以使用 std::sregex_iterator 迭代器批量遍历,解决 regex_search 只能返回第一个匹配结果的局限,适合批量提取多组结构化数据。
使用时传入字符串的首尾迭代器与正则对象,迭代器自增会自动定位到下一个匹配项;默认构造的迭代器为尾后迭代器,作为遍历结束的标志。迭代器解引用后为 std::smatch 对象,同样支持按下标访问各个捕获组。
示例:提取字符串中所有的数字序列
cpp
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string text = "2025-06-13 10:30:00,订单号12345,金额99.9元";
std::regex num_pattern(R"(\d+)");
// 构造迭代器起点与尾后终点
auto begin = std::sregex_iterator(text.begin(), text.end(), num_pattern);
auto end = std::sregex_iterator();
// 遍历所有匹配项
for (auto it = begin; it != end; ++it) {
std::cout << "匹配到数字:" << it->str() << std::endl;
}
return 0;
}
文本替换 std::regex_replace
std::regex_replace 用于将字符串中匹配正则规则的部分替换为指定内容,支持通过 $n(n 为捕获组序号)反向引用捕获组内容,$& 可引用整个匹配串,常用于文本格式化、内容脱敏、格式转换等场景。
核心参数说明:
-
第 1 个参数:原始输入字符串
-
第 2 个参数:
std::regex正则对象 -
第 3 个参数:替换格式字符串,支持捕获组反向引用
-
返回值:替换完成后的新字符串,原字符串不会被修改
默认会替换所有匹配项,添加 std::regex_constants::format_first_only 标志可仅替换第一个匹配项。
示例 1:基础全局替换,手机号脱敏
cpp
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string info = "手机号:13812345678";
std::regex phone_pattern(R"(\d{11})");
std::string result = std::regex_replace(info, phone_pattern, "***********");
std::cout << result << std::endl; // 输出:手机号:***********
return 0;
}
示例 2:捕获组反向引用,转换日期格式
cpp
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string date = "2025-06-13";
std::regex date_pattern(R"((\d{4})-(\d{2})-(\d{2}))");
// $1=年 $2=月 $3=日,转换为 月/日/年 格式
std::string new_date = std::regex_replace(date, date_pattern, "$2/$3/$1");
std::cout << new_date << std::endl; // 输出:06/13/2025
return 0;
}