使用语法进行正则表达式风格的捕获。






3.88/5 (4投票s)
使用语法而不是正则表达式来捕获文本。
引言
std::regex
最有用的功能之一是捕获文本的能力。然而,有时正则表达式对于更复杂的捕获来说力不从心。
示例
#include <iostream>
#include <regex>
int main()
{
try
{
std::regex rx("([A-Z_a-z]\\w*)(?:\\s*,\\s*([A-Z_a-z]\\w*))*");
std::string input = "111 One, 2, Three, Four 222 Five,Six,Seven Eight, 9, 10";
std::cregex_iterator iter(input.c_str(), input.c_str() + input.size(), rx);
std::cregex_iterator end;
for (; iter != end; ++iter)
{
for (std::size_t i = 0, size = (*iter).size(); i < size; ++i)
{
const std::string str = (*iter)[i].str();
if (!str.empty()) std::cout << str << '\n';
}
std::cout << '\n';
}
}
catch (const std::exception &e)
{
std::cout << e.what() << '\n';
}
return 0;
}
这段代码的输出如下
One One Three, Four Three Four Five,Six,Seven Five Seven Eight Eight
问题在于 "Five,Six,Seven" 这种情况,因为正则表达式只能捕获 "Five" 和 "Seven",省略了 "Six"。
解决方案
// http://www.benhanson.net/parsertl.html
#include "parsertl/enums.hpp"
#include "parsertl/generator.hpp"
#include <iostream>
#include "parsertl/search_iterator.hpp"
int main()
{
try
{
parsertl::rules grules(*parsertl::rule_flags::enable_captures);
parsertl::state_machine gsm;
lexertl::rules lrules;
lexertl::state_machine lsm;
grules.token("Name");
grules.push("list", "(Name) "
"| list ',' (Name)");
parsertl::generator::build(grules, gsm);
lrules.push("[A-Z_a-z]\\w*", grules.token_id("Name"));
lrules.push(",", grules.token_id("','"));
lrules.push("\\s+", lexertl::rules::skip());
lexertl::generator::build(lrules, lsm);
std::string input = "111 One, 2, Three, Four 222 Five,Six,Seven Eight, 9, 10";
lexertl::citerator liter(input.c_str(), input.c_str() + input.size(), lsm);
parsertl::csearch_iterator iter(liter, gsm);
parsertl::csearch_iterator end;
for (; iter != end; ++iter)
{
for (const auto &vec : *iter)
{
for (const auto &pair : vec)
{
std::cout << std::string(pair.first, pair.second) << '\n';
}
}
std::cout << '\n';
}
}
catch (const std::exception &e)
{
std::cout << e.what() << '\n';
}
return 0;
}
上面的代码输出如下
One One Three, Four Three Four Five,Six,Seven Five Six Seven Eight Eight
每个块的第一行相当于正则表达式中的 $0
。接下来的行是 $1
和 $2
。每个捕获都是一个向量,以便支持递归,因此在 "Five,Six,Seven" 的例子中,Six 和 Seven 存储在 $2
下,Five 存储在 $1
下。
历史
2018/03/22 创建。
2018/03/23 添加正则表达式示例。
2024/02/15:更新示例以使用 parsertl17 语法。