我如何执行此文本模式匹配

时间:2011-11-10 01:51:34

标签: c++ parsing boost pattern-matching boost-spirit

从[Spirit-general]列表中迁移

早上好,

我试图在4 std::strings之间解析一个相对简单的模式, 提取与模式匹配的任何部分到一个单独的 std::string

从抽象的意义上说,这就是我想要的:

s1=<string1><consecutive number>, s2=<consecutive number><string2>,
s3=<string1><consecutive number>, s4=<consecutive number><string2>

少抽象:

s1="apple 1", s2="2 cheese", s3="apple 3", s4="4 cheese"

实际内容:

s1="lxckvjlxcjvlkjlkje xvcjxzlvcj wqrej lxvcjz ljvl;x czvouzxvcu
j;ljfds apple 1 xcvljxclvjx oueroi xcvzlkjv; zjx", s2="xzljlkxvc
jlkjxzvl jxcvljzx lvjlkj wre 2 cheese", s3="apple 3", s4="kxclvj
xcvjlxk jcvljxlck jxcvl 4 cheese"

我如何进行此模式匹配?

感谢所有建议,

Alec Taylor

更新2

  

这是一个非常简单的解释,我只想解释一下   我试图解决的问题:

 std::string s1=garbagetext1+number1+name1+garbagetext4;
 std::string s3=garbagetext2+(number1+2)+name1+garbagetext5;
 std::string s5=garbagetext3+(number1+4)+name1+garbagetext6;

上下文

编辑

  

随意添加到stackoverflow(我遇到了一些麻烦   张贴那里)

     

我无法告诉你到目前为止我做过的事情,因为我不确定是不是   是在boost :: spirit库的功能范围内做什么的   我正在尝试

3 个答案:

答案 0 :(得分:4)

修改:重新 Update2

  

这是一个非常简单的解释,我只想解释一下   我试图解决的问题:

std::string s1=garbagetext1+number1+name1+garbagetext4;
std::string s3=garbagetext2+(number1+2)+name1+garbagetext5;
std::string s5=garbagetext3+(number1+4)+name1+garbagetext6;

它开始看起来像是一份工作:

  • 对'垃圾文本/名称'进行标记 - 您可以动态制作符号表并使用它来匹配模式(精神Lex和Qi的符号表(qi::symbol)可以促进它,但我觉得你可以用多种方式写出来)
  • 相反,使用前面建议的正则表达式(below,并且在邮件列表中至少使用两次)。

这是一个简单的想法:

 (\d+) ([a-z]+).*?(\d+) \2
  • \d+匹配“(子表达式)”(NUM1
  • 中的一系列数字
  • ([a-z]+)匹配名称(只选择了'name'的简单定义)
  • .*?跳过任何长度的垃圾,但在开始后续比赛之前尽可能少
  • \d+匹配另一个数字(数字序列)(NUM2
  • \2后跟相同 名称 backreference

你可以看到你如何缩小匹配列表以检查“潜在”命中。您只需要/后验证/看到NUM2 == NUM​​1 + 2

两个注释:

  1. 在尾部周围添加(...)+以允许重复匹配模式

      (\d+) ([a-z]+)(.*?(\d+) \2)+
    
  2. 您可能希望垃圾跳过(.*?)了解分隔符(通过执行 negative zerowidth assertions )以避免超过2个跳过分隔符(例如{ {1}}作为分隔模式)。为了清楚起见,我现在把它放在范围之外,这是要点:

    s\d+="

  3. Alec,以下是在回答你的问题时如何在Boost Spirit中做各种事情的展示案例。

    我必须对所需的输入结构做出假设;我假设

    • 空格是严格的(空格如图所示,没有换行符)
    • 序列号按递增顺序
    • 序列号在文本值中完全重现
    • 关键字'apple'和'cheese'严格改变
    • 关键字是否位于文本值中序列号之前或之后,也是严格更改

    注意下面的实施中有大约十几个地方,可能会做出明显不那么复杂的选择。例如,我可以硬编码整个模式(作为事实上的正则表达式?),假设输入中总是需要4个项目。但是我想

    但是,该解决方案具有很大的灵活性:

    • 关键字不是硬编码的,您可以例如轻松使解析器接受任何序列号的关键字
    • 注释显示如何在序列号不同步时生成自定义解析异常(不是预期的数字)
    • 目前接受序列号的不同拼写(即((?!s\d+=").)*? -- beware of potential performance degradation 即可。请查看Unsigned Integer Parsers以了解如何调整该行为的信息)
    • 输出结构是s01="apple 001"或struct的向量:

      vector<std::pair<int, std::string> >

      可以使用单struct Entry { int sequence; std::string text; };

    • 切换这两个版本

    该示例使用Boost Spirit Qi进行解析。 相反,Boost Spirit Karma用于显示解析结果:

    #if 1/0

    帖子中提供的 实际内容 的输出为:

    format((('s' << auto_ << "=\"" << auto_) << "\"") % ", ", parsed)
    

    代码。

    parsed: s1="apple 1", s2="2 cheese", s3="apple 3", s4="4 cheese"
    

答案 1 :(得分:2)

如果您可以使用c ++ 11编译器,使用AX 解析这些模式非常简单:

#include <axe.h>
#include <string>

template<class I>
void num_value(I i1, I i2)
{
    unsigned n;
    unsigned next = 1;
    // rule to match unsigned decimal number and compare it with another number
    auto num = axe::r_udecimal(n) & axe::r_bool([&](...){ return n == next; });
    // rule to match a single word
    auto word = axe::r_alphastr();
    // rule to match space characters
    auto space = axe::r_any(" \t\n");

    // semantic action - print to cout and increment next
    auto e_cout = axe::e_ref([&](I i1, I i2) 
    { 
        std::cout << std::string(i1, i2) << '\n'; 
        ++next; 
    });

    // there are only two patterns in this example
    auto pattern1 = (word & +space & num) >> e_cout;
    auto pattern2 = (num & +space & word) >> e_cout;

    auto s1 = axe::r_find(pattern1);
    auto s2 = axe::r_find(pattern2);
    auto text = s1 & s2 & s1 & s2 & axe::r_end();
    text(i1, i2);
}

要解析文本,只需调用num_value(text.begin(), text.end());无需更改即可解析unicode字符串。

我没有测试它。

答案 2 :(得分:0)

了解Boost.Regex。我已经在boost-users中看到了几乎相同的poosting,解决方案是使用正则表达式进行一些匹配工作。