当前位置: 首页 > news >正文

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

目录

1.正则表达式

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

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

1.4.量词:重复次数控制

1.5.位置锚定:边界断言

1.6.分组与捕获

1.7.逻辑选择与修饰符

2.C++ 的正则表达式库 简介

2.1.std::basic_regex 及其别名

2.2.std::match_results 及其别名

2.3.std::sub_match 及其别名

2.4.std::regex_match

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.*baababbaababb(整串)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匹配行首的aa^是无效的(位置锚点放中间没意义)
5|在多个分支之间选择cat|dog匹配catdog

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

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使用 POSIXawk工具的正则表达式语法。
grep使用 POSIXgrep工具的正则表达式语法,基于basic但允许\n分隔替代。
egrep使用 POSIXgrep -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_defaultregex_replace中,使用 ECMAScript 的替换语法。
format_sedregex_replace中,使用 POSIXsed工具的替换语法。
format_no_copyregex_replace中,不将未匹配的字符复制到输出。
format_first_onlyregex_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|PUT)捕获 HTTP 方法
空格分隔符
路径+参数([^?]*)捕获?之前的所有内容
查询字符串(可选)(?:\\?(.*))?非捕获组,内部捕获?后的内容
空格分隔符
HTTP 版本(HTTP/1\\.[01])捕获 HTTP/1.0 或 HTTP/1.1
换行符(可选)(?:\\n|\\r\\n)?非捕获,匹配\n\r\n
http://www.jsqmd.com/news/687373/

相关文章:

  • 2026年3月靠谱的实验室鞋厂家推荐,实验室鞋,实验室鞋厂家怎么选择 - 品牌推荐师
  • 清华PPT模板终极指南:3分钟打造专业学术汇报演示
  • VLC for Android电视版和ChromeOS:3大核心功能打造极致大屏观影体验
  • 用HC-08蓝牙模块和Arduino做个智能开关:完整项目搭建与手机APP控制教程
  • WinUtil技术深度解析:Windows系统自动化配置与优化框架
  • 剖析程序员怼怼,长沙编程课程性价比哪家高 - 工业设备
  • 玩机高手进阶:深入理解高通EDL模式与adb reboot edl的底层原理
  • 保姆级教程:手把手配置AUTOSAR CanTsyn模块,搞定车载ECU时间同步
  • 番茄小说离线阅读神器:fanqienovel-downloader让你的数字图书馆永不消失
  • 终极Marp移动端适配指南:在手机和平板上完美展示Markdown幻灯片
  • 突破传统:当视频字幕制作遇见智能革命
  • 从手机无线充电到音响分频器:聊聊身边那些‘藏起来’的LC谐振与滤波电路
  • 不写代码不配环境,手机说话让电脑24小时自动干活的AI智能数字人员工源码系统
  • 固家不锈钢橱柜质量好不好,有哪些信任背书可参考 - myqiye
  • NMNH是NMN十倍效率?新一代NAD⁺前体研究升温,抗衰市场或迎来升级窗口 - 资讯焦点
  • LeetCode刷题实战:用Python搞定最长递增子序列和最大子数组和(附完整代码)
  • 软件数据访问对象管理化的持久化抽象
  • GStreamer管道设计避坑指南:从USB摄像头采集到H.264 MP4,这些参数你调对了吗?
  • 哔哩下载姬完整指南:如何轻松获取B站高清视频资源
  • 告别pip install失败:用Anaconda虚拟环境丝滑部署TensorFlow-GPU(附国内源加速配置)
  • 2026年不错的二手车行企业推荐,杭州哪家品牌 - 工业推荐榜
  • MSP430开发入门:手把手教你用IAR EW430创建第一个工程(含F5529配置与常见下载错误解决)
  • EasyExcel单元格染色避坑指南:IndexedColors vs 自定义RGB,哪个更适合你的业务?
  • ERPNext无人值守安装脚本:如何5分钟完成企业级ERP系统部署
  • 最新YOLO实现的钢材表面缺陷实时检测平台(Flask+SocketIO+HTML_CSS_JS)
  • 别再手动折腾DLL了!用Winetricks一键搞定Linux下Wine环境配置(附QQ安装实战)
  • 从原理到优化:深入拆解Cesium自定义材质实现水面倒影的Shader技巧
  • 全自动微信聊天+公域获客+短视频分发,智能数字员工系统源码分享
  • 最新YOLO实现的草莓成熟度实时检测平台(Flask+SocketIO+HTML_CSS_JS)
  • Jetson Xavier NX 上唯一那个CAN口到底在哪?别再照着老教程找40针了