65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.88/5 (4投票s)

2018 年 3 月 22 日

CPOL
viewsIcon

5260

使用语法而不是正则表达式来捕获文本。

引言

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 语法。

© . All rights reserved.