C++ 正则表达式入门详解

正则表达式

正则表达式是编程时常用的一种字符串模式匹配工具,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 可匹配 cargarpar

锚点

锚点不匹配具体字符,仅匹配字符串的位置,用于限定匹配的起止边界:

  • ^:行首锚点,匹配字符串的起始位置,比如^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;
}
相关推荐
sulikey1 小时前
数据库系统概论 - 定义与查询 期末速成课笔记
数据库·笔记·期末考试·数据查询·期末速成·数据库系统概论·数据定义
xcLeigh1 小时前
鸿蒙平台 NixNote2 富文本笔记应用适配实战:从 Linux 到 鸿蒙PC 的 Electron 迁移
linux·笔记·harmonyos·富文本·nixnote2·evernote
kdxiaojie1 小时前
Linux 驱动研究 —— SPI (2)
linux·运维·笔记·学习
nan madol2 小时前
PolarDB 分布式版(PolarDB-X)
数据库
玖玥拾2 小时前
C/C++ 数据结构(一)基础概念、线性表链表
c语言·数据结构·c++·链表
starsky762382 小时前
NIO与BIO的区别
java·服务器·nio
星恒随风2 小时前
C++ 模板初阶:从泛型编程、函数模板到类模板,一篇打通基础概念
开发语言·c++·笔记·学习
johnny2332 小时前
数据库客户端:DBGate、DBX、dblab、SQLQueryStress、openhare、DBcooper、RedisME
数据库
郝学胜-神的一滴2 小时前
Qt 高级开发 031:QListWidget图标布局实战
开发语言·c++·qt·程序人生·软件构建·用户界面