c++oding="utf-8" ?>
提取单引号或双引号包围的字符串应优先用手工循环而非std::regex,因c++11标准regex对非贪婪匹配和反向引用支持不稳;若需处理转义则必须手写状态机跳过反斜杠转义序列,而string_view可避免substr拷贝。

提取单引号或双引号包围的字符串(基础正则)
标准 C++11 起支持 std::regex,但要注意:它在 libstdc++(GCC 默认)中长期存在部分功能缺失或性能问题,尤其是对非贪婪匹配(*?)支持不稳。MSVC 和 libc++(Clang/macOS)相对可靠。
若只需提取最外层引号内容,且引号成对、无嵌套、无转义,可用简单循环更稳妥:
std::vector<:string> extract_quoted(const std::string& s) {
std::vector<:string> res;
size_t i = 0;
while (i <ul>
<li>
<code>std::regex</code> 写法易错:比如 <code>R"((["'])(.*?))"</code> 依赖 <code></code> 反向引用匹配同类型引号,但 libstdc++ 中 <code>.*?</code> 可能退化为贪婪匹配</li>
<li>该循环版本不处理转义(如 <code>"a"b"</code>),若需支持,必须手动解析反斜杠,不能靠正则“偷懒”</li>
<li>引号类型混用(<code>"abc'</code>)会被忽略——这是设计选择,不是 bug</li>
</ul>
<h3>处理带转义的引号(如 <code>"a"b"</code>)</h3>
<p>一旦出现转义,正则基本失效(C++ 标准 regex 不支持可变宽度断言或递归匹配),必须手写状态机。</p>
<p>核心逻辑是:遇到反斜杠就跳过下一个字符,仅当未被转义的引号才触发结束。</p>
<pre class="brush:php;toolbar:false;">std::vector<:string> extract_escaped_quoted(const std::string& s) {
std::vector<:string> res;
for (size_t i = 0; i size_t start = i + 1;
++i; // 移动到第一个内容字符
while (i < s.size()) {
if (s[i] == '\' && i + 1 < s.size()) {
i += 2; // 跳过转义序列
continue;
}
if (s[i] == quote) break;
++i;
}
if (i < s.size()) {
res.push_back(s.substr(start, i - start));
}
// i 此时指向闭合引号,下次循环会自动跳过
}
return res;</:string></:string>
}
- 这个版本仍不处理 Unicode 转义(如
"u4f60")或八进制转义——那些属于 JSON/字符串字面量解析范畴,超出“提取”需求 - 注意
i += 2前必须检查i + 1 ,否则越界访问 - 如果源字符串含未闭合引号(
"unclosed),函数会静默跳过,不报错也不截断后续内容
用 std::string_view 避免拷贝(C++17+)
如果只是读取、不修改,且调用方能保证原始字符串生命周期长于结果,用 std::string_view 替代 std::string 返回能省掉多次 substr 拷贝。
只需把返回类型和 push_back 改为:
std::vector<:string_view> extract_quoted_views(const std::string& s) {
std::vector<:string_view> res;
// ...(循环逻辑不变)
res.push_back(std::string_view(s).substr(start, i - start));
// ...
}</:string_view></:string_view>
-
std::string_view不拥有数据,若传入的s是临时对象(如函数返回值),结果将悬空 - VS2019 及更新版、GCC 8+、Clang 7+ 均完整支持;旧编译器需回避
- 与
std::regex_iterator组合时无法直接用string_view,因 regex 接口只接受std::string或 C 字符串
常见错误:误用 std::regex_search 循环提取
很多人写类似这样的代码:
std::regex re(R"(["'](.*)["'])");
std::smatch m;
std::string::const_iterator begin = s.cbegin();
while (std::regex_search(begin, s.cend(), m, re)) {
res.push_back(m[1].str());
begin = m[0].second; // 错!应跳过整个匹配,而非仅 group 0
}
问题在于:m[0].second 指向整个匹配结尾(包括引号),但下一次搜索从那里开始,会漏掉紧邻的引号对(如 "a""b" 中第二个 " 被跳过)。
- 正确做法是设
begin = m[0].second,但前提是正则模式明确捕获引号并确保非重叠匹配 - 更安全的是用
std::regex_iterator,它内部已处理好迭代边界 - 但即便如此,libstdc++ 的
regex_iterator在某些模式下会死循环或崩溃——实测 GCC 9.4 仍存在此问题
真正需要正则的场景(比如同时匹配注释、引号、数字字面量),建议换用 PCRE2 或 Boost.Regex,别硬扛标准库实现缺陷。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











