c++oding="utf-8" ?>
提取子串应使用std::regex_search而非std::regex_match,因后者要求全字符串匹配,前者支持捕获组提取;配合std::smatch可获取指定组,循环调用可提取全部匹配项;转换为std::variant需显式指定类型并处理空匹配与安全数值解析。

std::regex_match 和 std::regex_search 哪个适合提取?
提取子串用 std::regex_search,不是 std::regex_match。后者要求整个字符串完全匹配,几乎无法用于提取;前者只要找到任意一处匹配即可,并支持 std::smatch 获取捕获组。
常见错误是误用 std::regex_match 后发现 smatch 为空——其实根本没进匹配分支。
- 提取单个捕获组(如邮箱本地部分):用
std::regex_search+smatch[1] - 需要全部匹配结果(如所有数字块):循环调用
std::regex_search,每次从smatch[0].second开始继续搜 - 正则带
^或$锚点时,regex_search仍能工作,但需确认是否真要限制位置
如何把不同提取结果塞进 std::variant?
std::variant 不支持运行时动态类型推导,必须提前声明所有可能类型。常见组合是:std::variant<:string int double std::monostate></:string>,其中 std::monostate 表示“没匹配到”或“转换失败”。
关键点在于:提取出的 std::string 需显式尝试转换,不能靠构造函数自动推导——variant 的构造是精确匹配的。
- 捕获组内容为空(
smatch[i].matched == false)→ 直接赋std::monostate{} - 想转
int:用std::from_chars(C++17),比std::stoi更安全(不抛异常、可判断截断) - 浮点数优先用
std::stod,但注意 locale 影响小数点解析;若输入确定为 C locale,std::from_chars也支持double
封装成通用函数要注意什么?
别写成模板参数全靠推导的“万能函数”。std::variant 类型必须显式指定,否则编译器无法合成构造逻辑。推荐接口形如:
template<typename... ts> std::variant<ts...> extract_to_variant(const std::string& s, const std::regex& re, size_t group_idx);</ts...></typename...>
但更实用的是固定 variant 类型 + 重载,例如:
- 对日期字符串,返回
std::variant<:string std::chrono::sys_days std::monostate></:string> - 对配置值,返回
std::variant<:string bool int64_t double></:string>,并约定第 1 组是原始字符串,第 2 组是数字(如有) - 避免在函数内做多组提取后“智能合并”——variant 是单值容器,一次只存一个东西
性能和兼容性坑点
std::regex 在 libstdc++(GCC)中性能差、功能弱(不支持 lookahead),MSVC 和 libc++ 相对好些。若正则复杂或高频调用,实际项目中建议换用 RE2 或 PCRE2 库。
另一个隐形坑:std::smatch 的 str() 返回的是临时 std::string,直接 move 进 variant 没问题;但若保存迭代器或 char* 就会悬空。
- 不要写
variant<const char> v = smatch[1].first;</const>——smatch生命周期一结束,指针就失效 - Clang 15+ 默认禁用
std::regex(-D_LIBCPP_ENABLE_CXX20_REGEX=ON 才开),老项目升级可能直接编译不过 - 如果只是简单分割或前缀匹配,
std::string_view::find+substr比正则快一个数量级,别条件反射上 regex
真正难的不是把字符串塞进 variant,而是决定哪些类型该放进 variant、每种类型对应哪组捕获、以及没匹配或转换失败时要不要记录原始错误位置——这些没法靠库函数自动解决。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











