C++正则表达式

正则表达式(Regular Expression,简称regex)是一种用于描述字符串模式的工具,可实现字符串的匹配、搜索、替换、提取等高效操作。C++自C++11起,通过标准库<regex>头文件正式引入正则表达式支持,无需依赖第三方库,即可完成复杂的文本处理任务。

C++标准库的正则表达式体系围绕清晰的结构化模型构建,核心是将正则模式封装为std::regex对象 ,通过标准算法(如regex_match、regex_search)应用于目标字符串,匹配结果则通过std::match_results等对象存储,支持对完整匹配结果和捕获子表达式的结构化访问。

1.正则表达式语法

1.1 基础匹配(普通字符与转义字符)

普通字符:直接匹配自身(如"abc"匹配字符串"abc");

转义字符:需加双反斜杠(\\),因为C++字符串中反斜杠本身是转义字符,常用转义字符如下:

cpp 复制代码
\\d:匹配任意数字(0-9),等价于[0-9];
\\D:匹配任意非数字,等价于[^0-9];
\\w:匹配任意字母、数字、下划线(a-z、A-Z、0-9、_);
\\W:匹配任意非字母、数字、下划线;
\\s:匹配任意空白字符(空格、制表符、换行符等);
\\S:匹配任意非空白字符;
\\.:匹配任意单个字符(除换行符\n外);
\\|:匹配竖线本身(竖线在正则中表示"或",需转义)。

1.2 量词(控制匹配次数)

量词用于指定前面的字符/子表达式匹配的次数,常用量词如下(优先级从高到低):

cpp 复制代码
?	    匹配0次或1次(可选)	    ab?c:匹配"ac"、"abc"
+	    匹配1次或多次(至少1次)	\\d+:匹配"1"、"123"、"4567"
*	    匹配0次或多次(任意次数)	abc*:匹配"ab"、"abc"、"abcc"
{n}	    匹配恰好n次	            \\d{3}:匹配"123"、"456"(仅3位数字)
{n,}	匹配至少n次	            \\d{2,}:匹配"12"、"123"、"1234"
{n,m}	匹配n到m次(包含n和m)    \\d{2,4}:匹配"12"、"123"、"1234"

注意:量词默认是"贪婪匹配" (尽可能多匹配),在量词后加?可改为"非贪婪匹配"(尽可能少匹配),例如:"a.*?"匹配最短的以a开头的子串。

1.3 捕获组(分组匹配与提取)

用()将正则表达式的一部分括起来,称为捕获组,可实现"分组匹配",后续可通过match1、match2等获取分组内容,常用于提取复杂字符串中的特定部分。

示例:正则表达式std::regex re("(\\d{4})-(\\d{2})-(\\d{2})");,可匹配日期格式(如2026-04-17),其中:

cpp 复制代码
match[0]:整个日期字符串(2026-04-17);
match[1]:年份(2026);
match[2]:月份(04);
match[3]:日期(17)。

补充:非捕获组(?:...)------仅用于分组,不捕获结果,可减少不必要的内存开销,例如R"(?:abc)+",匹配多次abc,但不单独捕获abc。

1.4 边界匹配(精准定位)

用于精准定位匹配的位置,避免部分匹配导致的错误,常用边界符号:

  • ^:匹配字符串的开头(如^abc匹配以abc开头的字符串);

  • **:匹配字符串的结尾**(如abc匹配以abc结尾的字符串);

  • \\b:单词边界(匹配单词的开头或结尾,如\\bhello\\b匹配独立的hello单词,不匹配helloworld)。

1.5 逻辑匹配(或、非、与)

  • 或(|)匹配多个模式中的一个,例如 abc|def 匹配abc或def;

  • 非(\^...匹配不在括号内的任意字符,例如 \^0-9 匹配非数字字符;

  • 与(默认):正则表达式中,多个字符/子表达式连续书写,即为"与"关系,例如abc匹配a且b且c连续出现。

2.核心组件

2.1 正则表达式对象:std::regex

std::regex是C++正则表达式的核心类,用于封装编译后的正则表达式模式,构造时需指定正则字符串和可选的语法/匹配标志。

构造方式:直接传入正则字符串,可搭配语法标志(如ECMAScript、icase(不区分大小写)等)

语法与匹配选项常量说明:

(1)Grammar option(语法选项):指定正则表达式的语法规则,一次只能选一种,默认是 ECMAScript。

  • ECMAScript:默认选项,使用修改过的 ECMAScript 语法,与 JavaScript/Java 正则兼容,在绝大多数 C++ 开发场景中最通用;

  • basic:是 POSIX BRE(基本正则),语法老旧,不支持 `+` `?` `{m,n}`,用于兼容传统 `grep` 等老旧 POSIX 工具;

  • extended:是 POSIX ERE(扩展正则),支持 `+` `?` `{m,n}`,更接近现代语法,兼容 `grep -E` / `egrep`;

  • awk:采用 POSIX awk 工具的正则语法,兼容 awk 脚本的写法;

-grep:类似 basic,但额外把换行符 `\n` 作为"或"操作符;

  • egrep:类似 extended,但把 `\n` 和制表符 `\t` 都作为分隔符,以兼容 `grep -E` / `egrep` 的跨行匹配语义。

(2)Grammar variation(语法变体 / 匹配选项):修饰匹配行为(如忽略大小写、多行模式等),可以用 | 与语法选项组合。

  • icase:忽略大小写匹配,可以同时匹配 hello/Hello/HELLO 等不同大小写形式;

  • nosubs禁用捕获组 ,匹配结果中不存储子表达式内容,适用于只需要判断是否匹配而无需提取分组的场景,可以提升性能;

  • optimize:优化匹配速度,编译正则时会做额外处理(耗时更长),适合正则会被重复调用的场景(如循环中),以空间换时间;

  • collate:使字符范围 a-b 受 locale 影响(如非 ASCII 字符的排序规则),用于处理本地化字符(如中文、法语等);

  • multiline(C++17 引入):多行模式,让 `^` 匹配每行开头、`$` 匹配每行结尾,而非仅匹配整个字符串的首尾,用于处理多行文本(如日志文件)时的逐行匹配需求。

cpp 复制代码
#include <iostream>
#include <regex>
#include <string>
using namespace std;

int main() {
    // 邮箱正则表达式
    // R 的作用是引入原始字符串字面量,让字符串中的反斜杠\、双引号等特殊字符不再被转义
    regex email_regex(R"(^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$)");
    //regex email_regex("(^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$)");
    string email;

    cout << "请输入邮箱地址:";
    cin >> email;

    try {
        smatch match_res;
        if (regex_match(email, match_res, email_regex)) {
            cout << "邮箱格式合法!匹配结果:" << match_res.str() << endl;
        } else {
            cout << "邮箱格式非法!" << endl;
        }
    } catch (const regex_error& e) {
        cout << "正则表达式错误:" << e.what() << endl;
    }

    return 0;
}

2.2 匹配结果存储:std::match_results

用于存储正则匹配的结果,本质是一个包含子匹配(sub_match)的容器,常用的实例化类型有3种,根据目标字符串类型选择:

(1)std::smatch:目标字符串为std::string

最常用,匹配结果与string对应,可通过str()获取字符串形式

cpp 复制代码
std::regex_match(const std::string& s, std::smatch& m, const std::regex& e);
std::regex_search(const std::string& s, std::smatch& m, const std::regex& e);
cpp 复制代码
#include <regex>
#include <iostream>
#include <string>

int main() {
    std::string text = "出生日期: 1990-05-20";
    std::regex re(R"((\d{4})-(\d{2})-(\d{2}))");
    std::smatch match;
    
    if (std::regex_search(text, match, re)) {
        std::cout << "完整匹配: " << match.str() << std::endl;      // 1990-05-20
        std::cout << "年份: " << match[1].str() << std::endl;       // 1990
        std::cout << "月份: " << match[2].str() << std::endl;       // 05
        std::cout << "日期: " << match[3].str() << std::endl;       // 20
    }
    return 0;
}

(2)std::cmatch:目标字符串为const char*

适用于C风格字符串,使用方式与smatch一致

cpp 复制代码
std::regex_match(const char* s, std::cmatch& m, const std::regex& e);
std::regex_search(const char* s, std::cmatch& m, const std::regex& e);

(3)std::wsmatch:目标字符串为std::wstring

用于宽字符字符串,处理中文等多字节字符场景

cpp 复制代码
std::regex_match(const std::wstring& s, std::wsmatch& m, const std::wregex& e);
std::regex_search(const std::wstring& s, std::wsmatch& m, const std::wregex& e);

核心用法:匹配成功后,通过下标访问匹配结果,match0表示整个匹配的字符串,match1、match2...表示第1、2...个捕获组的内容(捕获组由正则中的()定义)。

2.3 核心匹配函数

C++提供3个核心正则匹配函数,分别对应不同的匹配场景,需根据需求选择,避免混用导致逻辑错误:

(1)std::regex_match

功能:完整匹配------要求目标字符串从头到尾完全符合正则模式,一个字符都不能多、不能少,常用于字符串格式验证(如邮箱、手机号、日期等)。

cpp 复制代码
// 函数原型
bool regex_match(const std::string& s, const std::regex& e);
bool regex_match(const std::string& s, std::smatch& m, const std::regex& e);
cpp 复制代码
#include <regex>
#include <iostream>

int main() {
    std::regex email_regex(R"([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})");
    
    std::string valid_email = "user@example.com";
    std::string invalid_email = "my email is user@example.com";
    
    if (std::regex_match(valid_email, email_regex)) {
        std::cout << "邮箱格式正确" << std::endl;  // 执行
    }
    
    if (!std::regex_match(invalid_email, email_regex)) {
        std::cout << "邮箱格式不正确" << std::endl;  // 执行
    }
    
    // 提取捕获组
    std::regex date_regex(R"((\d{4})-(\d{2})-(\d{2}))");
    std::string date = "2024-12-25";
    std::smatch match;
    
    if (std::regex_match(date, match, date_regex)) {
        std::cout << "年: " << match[1] << ", 月: " << match[2] << ", 日: " << match[3] << std::endl;
    }
}

(2)std::regex_search

功能:部分匹配 (搜索匹配)------在目标字符串中搜索第一个符合正则模式的子串,无需整个字符串匹配,常用于提取子串、判断字符串中是否包含某类内容(如日志中提取IP地址)。

cpp 复制代码
// 函数原型
bool regex_search(const std::string& s, const std::regex& e);
bool regex_search(const std::string& s, std::smatch& m, const std::regex& e);
bool regex_search(const char* first, const char* last, std::cmatch& m, const std::regex& e);
cpp 复制代码
#include <regex>
#include <iostream>
#include <string>

int main() {
    std::string log = "2024-12-25 10:30:45 ERROR: Connection failed from 192.168.1.100";
    std::regex ip_regex(R"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})");
    std::smatch match;
    
    if (std::regex_search(log, match, ip_regex)) {
        std::cout << "找到IP: " << match[0] << std::endl;  // 192.168.1.100
    }
    
    // 搜索所有匹配(配合迭代器)
    std::string text = "苹果15元,香蕉8元,橙子12元";
    std::regex price_regex(R"(\d+元)");
    auto begin = std::sregex_iterator(text.begin(), text.end(), price_regex);
    auto end = std::sregex_iterator();
    
    for (auto it = begin; it != end; ++it) {
        std::cout << "价格: " << it->str() << std::endl;
    }
    // 输出: 15元, 8元, 12元
}

注意:默认从字符串开头开始搜索,可通过传入迭代器指定搜索范围(如跳过前导空格);若需搜索所有匹配子串,需结合std::sregex_iterator使用。

(3)std::regex_replace

功能:替换匹配 ------在目标字符串中,将所有符合正则模式的子串替换为指定字符串,支持替换为捕获组的内容,常用于文本清洗(如去除空格、替换敏感字符)。

cpp 复制代码
// 函数原型
std::string regex_replace
(const std::string& s, const std::regex& e, const std::string& fmt);
std::string regex_replace(const std::string& s, const std::regex& e, 
const std::string& fmt, std::regex_constants::match_flag_type flags);

fmt 在 std::regex_replace 中指定替换格式字符串
cpp 复制代码
#include <regex>
#include <iostream>
#include <string>

int main() {
    // 基础替换:去除所有空格
    std::string text1 = "Hello   World   C++";
    std::regex space_regex(R"(\s+)");
    std::string result1 = std::regex_replace(text1, space_regex, " ");
    std::cout << result1 << std::endl;  // Hello World C++
    
    // 使用捕获组:格式化电话号码
    std::string phone = "13812345678";
    std::regex phone_regex(R"((\d{3})(\d{4})(\d{4}))");
    // $1、$2、$3 是反向引用,用于引用正则表达式中捕获组匹配到的内容。
    std::string formatted = std::regex_replace(phone, phone_regex, "$1-$2-$3");
    std::cout << formatted << std::endl;  // 138-1234-5678
    
    // 敏感信息脱敏
    std::string id_card = "身份证号:11010119900307663X";
    std::regex id_regex(R"((\d{6})\d{8}(\d{3}[0-9X]))");
    std::string masked = std::regex_replace(id_card, id_regex, "$1********$2");
    std::cout << masked << std::endl;  // 身份证号:110101********63X
    
    // 清洗HTML标签
    std::string html = "<p>Hello <b>World</b></p>";
    std::regex tag_regex(R"(<[^>]*>)");
    std::string clean = std::regex_replace(html, tag_regex, "");
    std::cout << clean << std::endl;  // Hello World
}

2.4 迭代器(批量匹配)

当需要提取目标字符串中所有符合正则模式的子串时,需使用正则迭代器,常用std::sregex_iterator(对应std::string),可遍历所有匹配结果。

核心逻辑:通过迭代器初始化时传入目标字符串和正则对象,循环遍历迭代器,直到迭代器指向end(),每次迭代可获取一个匹配结果(smatch对象)。

cpp 复制代码
// 构造函数:指定搜索范围和正则表达式
std::sregex_iterator it(字符串起始迭代器, 字符串结束迭代器, 正则表达式对象);
std::sregex_iterator end;  // 默认构造的尾后迭代器

// 遍历所有匹配
for (auto it = std::sregex_iterator(str.begin(), str.end(), regex); 
         it != std::sregex_iterator(); 
         ++it) {
    std::smatch match = *it;
    // 处理匹配结果
}
cpp 复制代码
#include <regex>
#include <iostream>
#include <string>

int main() {
    std::string text = "订单号:ORD-001,金额:299元;订单号:ORD-002,金额:450元;订单号:ORD-003,金额:128元";
    std::regex order_regex(R"(ORD-(\d{3}))");
    // 创建正则迭代器
    auto begin = std::sregex_iterator(text.begin(), text.end(), order_regex);
    auto end = std::sregex_iterator();  // 尾后迭代器
    
    // 遍历所有匹配结果
    for (auto it = begin; it != end; ++it) {
        std::smatch match = *it;
        std::cout << "完整匹配: " << match.str() << std::endl;
        std::cout << "捕获组(订单号后3位): " << match[1].str() << std::endl;
        std::cout << "---" << std::endl;
    }
    return 0;
}

3. std::regex_error 错误处理详解

std::regex_error 是 C++ 标准库中专门用于报告正则表达式相关异常的类,继承自std::runtime_error。

cpp 复制代码
#include <regex>
#include <iostream>

class regex_error : public std::runtime_error {
public:
    // 构造函数(内部使用,用户无法直接构造)
    explicit regex_error(regex_constants::error_type ecode);
    
    // 返回错误码枚举值
    regex_constants::error_type code() const noexcept;
    
    // 返回错误描述字符串(继承自 std::runtime_error)
    const char* what() const noexcept override;
};
cpp 复制代码
#include <regex>
#include <iostream>
#include <string>

void test_regex(const std::string& pattern) {
    std::cout << "测试模式: \"" << pattern << "\"" << std::endl;
    try {
        std::regex re(pattern);
        std::cout << "✓ 编译成功" << std::endl;
    } catch (const std::regex_error& e) {
        std::cout << "✗ 编译失败: " << e.what() << std::endl;
        std::cout << "  错误码: " << e.code() << std::endl;
    }
    std::cout << std::endl;
}

int main() {
    test_regex("(unclosed");      // 括号不匹配
    test_regex("*");              // 量词前无内容
    test_regex("a{1,2");          // 花括号语法错误
    test_regex("a{5,2}");         // 花括号范围无效
    test_regex("\\c");            // 无效转义序列
    test_regex("[[:invalid:]]");  // 无效字符类名
    test_regex("[z-a]");          // 字符范围无效
    test_regex("(valid)\\d+");    // 正确正则
    return 0;
}
cpp 复制代码
error_brack	    括号不匹配	        "(" 缺少 ")"、"[]" 不闭合
error_badrepeat	重复量词前无表达式	"*"、"+?"、"{2}" 前面没有可重复的内容
error_brace	    花括号 {} 错误	    "{1,2" 缺少 }、"{1,a}" 非数字
error_brace_range	花括号范围无效	"{5,2}" 下限大于上限
error_escape	无效的转义序列	    "\\c"(无效转义)、"\\u123"(不完整)
error_content	无效的字符内容	    字符类内无效使用,如 "[a-"
error_collate	无效的排序元素	    "[[.xx.]]" 无效的排序名
error_ctype	    无效的字符类名	    "[[:invalid:]]"
error_range	    字符范围无效	        "[z-a]" 起始大于结束
error_parsing	解析失败(通用错误)	语法解析器遇到无法识别的内容
error_space	    内存不足	            编译后的正则表达式太大,超出内存限制
error_stack	    栈溢出	            正则表达式过于复杂,递归深度超限
error_complexity	复杂度超限	    匹配操作过于耗时(如灾难性回溯)
error_collate	无效的排序元素	    同上(某些实现中重复定义)
相关推荐
何以解忧,唯有..2 小时前
Python 元组(tuple)详解:使用、遍历与排序
开发语言·python
KANGBboy2 小时前
Python eval安全隐患
开发语言·python
IT爱学堂2 小时前
java版数据结构和算法+AI算法和技能学习指南
java·开发语言
淼澄研学3 小时前
Python结合大模型挖掘搜题长尾关键词实操指南
开发语言·python
FfHUCisI3 小时前
Golang - 信号量模式(Semaphore Pattern)
开发语言·后端·golang
m0_547486663 小时前
《Java程序设计与实践 》全套PPT课件2026
java·开发语言
码匠许师傅4 小时前
【C++ 面试真题】聊聊 C++ 中的 lambda 表达式
java·c++·面试
依然鸣4 小时前
PTA团体程序设计天梯赛L1真题讲解L1-085-088
数据结构·c++·经验分享·算法·深度优先·pat考试
灵析表格4 小时前
灵析表格财务函数深度实用性分析与实操教程
开发语言·ai·json·excel·wps