服务器设计 之 【正则表达式及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_match2.5. std::regex_search2.6.std::regex_replace2.7.std::regex_iterator2.8.std::regex_token_iterator2.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-45671.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.*baababbaababb整串aab然后ab.*divtext/divdivtext/div整串div然后/div.*one twoone two整串one然后two1.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 为 222非捕获组 (?:...)只分组不捕获→ 不占用编号性能稍好正则更清晰(?: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第二次匹配结果python4反向引用的编号编号按捕获组出现的顺序左括号的位置非捕获组 (?:...) 不计入\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]是ab[cd]3量词*?{n,m}紧贴在它前面的一个原子字符、字符类或分组上a匹配一个或多个a[ab]匹配一个或多个由a或b组成的序列ab匹配ab而不是(ab)4连接拼接两个相邻的原子中间没有操作符ab匹配a后紧跟b空格在大部分模式里就是一个普通字符5锚点^$\b\B匹配位置不消耗字符^a匹配行首的aa^是无效的位置锚点放中间没意义5或|在多个分支之间选择cat|dog匹配cat或dog2.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_matchconst_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)内进行匹配结果存入mbool 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风格字符串上匹配结果存入mbool 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上匹配结果存入mbool 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风格字符串上匹配结果存入m2.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)内搜索结果存入mbool 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风格字符串上搜索结果存入mbool 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上搜索结果存入mbool 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)内的替换结果写入输出迭代器outstringT 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(C17) 在多行模式下^和$匹配行的开头和结尾而非整个字符串的开头和结尾。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中使用 POSIXsed工具的替换语法。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); }2optimize 标志的误导性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?userxiaoming123123 HTTP/1.1\r\n std::string str GET /bitejiuyeke/login?userxiaoming123123 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