如何用C++ std::regex_match提取状态机语句中的多组有效内容?
改进C++ std::regex_match提取状态机元素的实现
我需要从状态机定义字符串中提取核心元素(源状态A、事件e、守卫guard、动作action、目标状态B),用于生成PlantUML状态图。之前用字符串分割/修剪的方式处理,现在尝试用C++的std::regex_match实现,但当前正则提取的结果会带入多余字符(如+、[]、/),请求改进。
示例状态机字符串
hsm::state<A> = hsm::state<B> hsm::state<A> + hsm::event<e> = hsm::state<B> hsm::state<A> [guard] = hsm::state<B> hsm::state<A> / action = hsm::state<B> hsm::state<A> + hsm::event<e> [guard] = hsm::state<B> hsm::state<A> [guard] / action = hsm::state<B> hsm::state<A> + hsm::event<e> [guard] / action = hsm::state<B>
现有代码
#include <fstream> #include <iostream> #include <string> #include <regex> using namespace std; int main(int argc, char* argv[]) { std::regex re{"(.*hsm::state<.[^>]*>)(.*hsm::event<.[^>]*>)?(.*\\[.[^>]*\\])?(.*/.[^=]*)?.*(hsm::state<.[^>]*>).*"}; ifstream file(argv[1]); for (std::string line; getline(file, line);) { cout << "[" << line << "]" << endl; std::smatch match; if (std::regex_match(line, match, re)) { const char* names[] = {"", "source", "event ", "guard ", "action", "target", "", "", ""}; for (size_t i = 1; i < match.size(); ++i) { cout << " m[" << i << "]: " << names[i] << ": [" << match[i] << "]" << endl; } } } }
改进思路
核心是调整正则表达式,通过精准捕获组直接提取需要的核心内容,而非捕获包含多余符号的整段文本。具体调整点:
- 对源状态、目标状态,直接捕获
<>内部的状态名(如A、B) - 对事件,跳过
+ hsm::event<前缀,直接捕获<>内部的事件名(如e) - 对守卫,跳过
[前缀和]后缀,直接捕获括号内的守卫内容 - 对动作,跳过
/前缀,直接捕获动作内容
改进后的代码
#include <fstream> #include <iostream> #include <string> #include <regex> using namespace std; int main(int argc, char* argv[]) { // 正则说明: // 1. 源状态:捕获hsm::state<...>中的...部分 // 2. 事件:可选,匹配+ hsm::event<...>并捕获...部分 // 3. 守卫:可选,匹配[...]并捕获...部分 // 4. 动作:可选,匹配/ ... 并捕获...部分(直到=前) // 5. 目标状态:捕获hsm::state<...>中的...部分 std::regex re{R"(hsm::state<([^>]*)>\s*(?:\+\s*hsm::event<([^>]*)>)?\s*(?:\[([^\]]*)\])?\s*(?:/\s*([^=]*))?\s*=\s*hsm::state<([^>]*)>)"}; ifstream file(argv[1]); for (std::string line; getline(file, line);) { cout << "[" << line << "]" << endl; std::smatch match; if (std::regex_match(line, match, re)) { const char* names[] = {"", "source", "event", "guard", "action", "target"}; for (size_t i = 1; i < match.size(); ++i) { if (!match[i].str().empty()) { cout << " m[" << i << "]: " << names[i] << ": [" << match[i].str() << "]" << endl; } } } } return 0; }
关键正则解释
hsm::state<([^>]*)>:匹配源状态定义,([^>]*)捕获<>内的状态名(非>的任意字符)(?:\+\s*hsm::event<([^>]*)>)?:非捕获组包裹事件匹配,?:表示不捕获整个前缀,只捕获<>内的事件名,?表示该部分可选(?:\[([^\]]*)\])?:非捕获组包裹守卫匹配,捕获[]内的内容,可选(?:/\s*([^=]*))?:非捕获组包裹动作匹配,捕获/后到=前的内容,可选\s*=\s*hsm::state<([^>]*)>:匹配等号和目标状态,捕获目标状态名
测试效果
对于所有示例字符串,提取的内容均为干净的核心值:
- 源状态:
A - 事件(存在时):
e - 守卫(存在时):
guard - 动作(存在时):
action - 目标状态:
B
内容的提问来源于stack exchange,提问作者David Fleury
相关产品推荐
相关产品推荐

