使用std::regex解析字符串触发栈溢出,是否为标准库缺陷?
问题描述
我尝试用std::regex将字符串拆分为多个子部分,使用的正则表达式如下:
"(^([\s\S]+?)\s*(\W)\s*[\r\n]+A\.\s*((?:.+(?:[\r\n](?![B-D]\.))*)+)[\r\n]+B\.\s*((?:.+(?:[\r\n](?![CD]\.))*)+)[\r\n]+C\.\s*((?:.+(?:[\r\n](?!D\.))*)+)[\r\n]+D\.\s*([\s\S]+)$)"
多数情况下该正则运行正常,但处理特定文本时抛出栈溢出异常:
Unhandled exception: Unhandled exception at 0x00007FF7C93FCF66 in Convert.exe: 0xC00000FD: Stack overflow (parameters: 0x0000000000000001, 0x000000FE83A43F98).
异常发生在xtr1common文件的这段代码处:
in the xtr1common:
_EXPORT_STD _NODISCARD constexpr bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated(); }
#endif // _HAS_CXX20
测试使用的字符串:
The portfolio manager from your division thought it might be helpful to the project teams if she delivered a short presentation on the elements in her portfolio. A number of team members, after receiving the e- mail announcement for the presentation, come to you and ask if this meeting is worth their time. After all isn’t a portfolio just a big project? As a Senior Project manager your best response would be: A. You’re right. The meeting probably would be a waste of your time B. Not really. A portfolio is a group of related projects managed together to achieve synergies between the projects and establish common methods and procedures. C. Not really. A portfolio can be a group of programs, projects, or sub-projects designed to help the organization meet specific business goals D. Not really. A portfolio is a collection of documents, methods, and procedures that help us manage projects
完整测试代码:
#include <string> #include <regex> #include <iostream> using std::string; using std::regex; using std::smatch; int main() { string s = R"(The portfolio manager from your division thought it might be helpful to the project teams if she delivered a short presentation on the elements in her portfolio. A number of team members, after receiving the e- mail announcement for the presentation, come to you and ask if this meeting is worth their time. After all isn’t a portfolio just a big project? As a Senior Project manager your best response would be: A. You’re right. The meeting probably would be a waste of your time B. Not really. A portfolio is a group of related projects managed together to achieve synergies between the projects and establish common methods and procedures. C. Not really. A portfolio can be a group of programs, projects, or sub-projects designed to help the organization meet specific business goals D. Not really. A portfolio is a collection of documents, methods, and procedures that help us manage projects)"; string pattern = R"(^([\s\S]+?)\s*(\W)\s*[\r\n]+A\.\s*((?:.+(?:[\r\n](?![B-D]\.))*)+)[\r\n]+B\.\s*((?:.+(?:[\r\n](?![CD]\.))*)+)[\r\n]+C\.\s*((?:.+(?:[\r\n](?!D\.))*)+)[\r\n]+D\.\s*([\s\S]+)$)"; regex questionRegex(pattern, std::regex_constants::ECMAScript /* | std::regex_constants::collate */); smatch question_match; try { if (regex_search(s, question_match, questionRegex)) { std::cout << "Query: " << question_match[1] << "\n"; std::cout << "A: " << question_match[2] << "\n"; std::cout << "B: " << question_match[3] << "\n"; std::cout << "C: " << question_match[4] << "\n"; std::cout << "D: " << question_match[5] << "\n"; } } catch (std::regex_error & error) { std::cout << "error" << error.what(); } return 0; }
请问这种情况是否意味着std::regex存在缺陷?
问题分析与解决
这不是std::regex的缺陷,问题出在你写的正则表达式上——嵌套量词导致了回溯爆炸,进而触发栈溢出。
看你正则里的这类结构:((?:.+(?:[\r\n](?![B-D]\.))*)+),外层的+和内层非捕获组里的*形成嵌套量词。当正则引擎尝试匹配时,会产生大量回溯分支,持续消耗栈空间,最终导致栈溢出。
另外,原正则还有两处明显问题:
- 分组索引对应错误,比如你输出时用
question_match[2]对应选项A,但实际[2]是\W的捕获组,会导致输出内容错误; - 匹配换行的逻辑不够兼容,未考虑Windows平台的
\r\n格式。
优化后的正则表达式
^([\s\S]+?)\s*(\W)\s*\r?\nA\.\s*((?:(?!\r?\n[B-D]\.).)*)\r?\nB\.\s*((?:(?!\r?\n[CD]\.).)*)\r?\nC\.\s*((?:(?!\r?\nD\.).)*)\r?\nD\.\s*([\s\S]+)$
优化点说明:
- 把嵌套的
(.+(?:...)*)+改成((?:(?!...).)*),用否定前瞻精确控制匹配范围,彻底避免回溯爆炸; - 用
\r?\n兼容Windows和Unix的换行格式; - 简化结构,减少正则引擎的计算负担。
修正后的测试代码
#include <string> #include <regex> #include <iostream> using std::string; using std::regex; using std::smatch; int main() { string s = R"(The portfolio manager from your division thought it might be helpful to the project teams if she delivered a short presentation on the elements in her portfolio. A number of team members, after receiving the e- mail announcement for the presentation, come to you and ask if this meeting is worth their time. After all isn’t a portfolio just a big project? As a Senior Project manager your best response would be: A. You’re right. The meeting probably would be a waste of your time B. Not really. A portfolio is a group of related projects managed together to achieve synergies between the projects and establish common methods and procedures. C. Not really. A portfolio can be a group of programs, projects, or sub-projects designed to help the organization meet specific business goals D. Not really. A portfolio is a collection of documents, methods, and procedures that help us manage projects)"; // 优化后的正则,修正分组对应关系 string pattern = R"(^([\s\S]+?)\s*(\W)\s*\r?\nA\.\s*((?:(?!\r?\n[B-D]\.).)*)\r?\nB\.\s*((?:(?!\r?\n[CD]\.).)*)\r?\nC\.\s*((?:(?!\r?\nD\.).)*)\r?\nD\.\s*([\s\S]+)$)"; regex questionRegex(pattern, std::regex_constants::ECMAScript); smatch question_match; try { if (regex_search(s, question_match, questionRegex)) { std::cout << "Query: " << question_match[1] << "\n"; std::cout << "A: " << question_match[3] << "\n"; // 修正索引,对应A选项的捕获组 std::cout << "B: " << question_match[4] << "\n"; std::cout << "C: " << question_match[5] << "\n"; std::cout << "D: " << question_match[6] << "\n"; } } catch (std::regex_error & error) { std::cout << "error: " << error.what() << "\n"; } return 0; }
额外说明
只要解决了回溯爆炸的问题,栈溢出就会消失。如果你的编译器支持C++20及以上,可以用std::format简化输出,但核心问题始终是正则表达式的结构设计。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

