You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Spirit Qi解析逗号分隔列表的问题求助

问题描述

我正在用Boost Spirit Qi解析可选为空的逗号分隔列表,格式为"a b c, a b c,...",目标是解析为std::vector。目前解析逻辑基本可用,但存在两个问题:

  • 空字符串(或全空格字符串)无法解析为空列表;
  • 末尾带有逗号的字符串会被判定为解析成功(不符合预期,本应无效)。

当前使用的列表解析规则:

abc_rule >> *( ',' >> abc_rule )

我疑惑为何该规则允许末尾单独存在逗号——按规则逗号后应匹配abc_rule才对,难道是解析不够贪婪?另外,尝试在规则前添加omit[*qi::space] |以支持空列表的操作并未生效。

相关代码:

#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

struct Out
{
    std::string a, b, c;
};
BOOST_FUSION_ADAPT_STRUCT( Out, a, b, c )

int main()
{
    using It = std::string_view::const_iterator;
    qi::rule<It, std::string()> identifier_rule = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" );

    #if 1
    qi::rule<It, Out()> abc_rule                     //
        = qi::omit[*qi::space] >> identifier_rule    //
          >> qi::omit[+qi::space] >> identifier_rule //
          >> qi::omit[+qi::space] >> identifier_rule //
          >> qi::omit[*qi::space];
    #else
    qi::rule<It, Out()> abc_rule = qi::skip( qi::space )[identifier_rule >> identifier_rule >> identifier_rule];
    #endif

    qi::rule<It, std::vector<Out>()> abc_list_rule = abc_rule >> *( ',' >> abc_rule );

    for( std::string_view test : {
             "a0 b0 c0,a1 b1 c1",            // valid
             "a0 b0 c0,",                    // !!!should be invalid - due to comma at end
             "XXXX",                         // invalid
             "a0 b0 c0",                     // valid
             "a0",                           // invalid
             "   ",                          // !!!should be valid -> empty list
             " , ",                          // invalid
         } )
    {
        std::cout << "Parsing: [" << test << "]\n";
        It f = test.begin(), l = test.end();
        if( std::vector<Out> v; qi::parse( f, l, abc_list_rule, v ) )
        {
            std::cout << "Parsed:\n";
            std::string left( f, l );
            if( !left.empty() )
                std::cout << "left : [" << std::string( f, l ) << "]\n ";
            for( auto& o : v )
            {
                std::cout << "\nA: " << std::quoted( o.a ) << "\n"
                          << "B: " << std::quoted( o.b ) << "\n"
                          << "C: " << std::quoted( o.c ) << "\n";
            }
        }
        else
            std::cout << "Failed to parse " << std::quoted( test ) << " left: [" << std::string( f, l ) << "]\n";
    }
}

解决方案

问题2:末尾逗号被允许的原因及修复

你看到末尾带逗号的字符串被“成功解析”,本质是误解了qi::parse的行为:qi::parse只要规则匹配了输入的前缀部分就会返回true,不会强制解析到输入末尾。比如解析"a0 b0 c0,"时,规则匹配了前面的a0 b0 c0,剩下的,未被处理,但qi::parse依然返回true。

修复方式有两种:

  1. 强制解析到输入末尾:在规则末尾添加qi::eoi(输入结束标记),确保整个输入都被匹配:
    qi::rule<It, std::vector<Out>()> abc_list_rule = (abc_rule >> *(',' >> abc_rule)) >> qi::eoi;
    
  2. 调用时验证迭代器:即使qi::parse返回true,也要检查迭代器是否到达输入末尾(f == l),只有两者都满足才判定为解析成功。

问题1:支持空列表的修复

原来的规则abc_rule >> *(',' >> abc_rule)要求至少有一个abc_rule匹配,因此空字符串/全空格字符串无法通过。要支持空列表,需将规则改为允许0个或多个列表项,同时兼容分隔符:

  • 方式1:使用Spirit内置的%(列表解析)结合qi::optional:
    qi::rule<It, std::vector<Out>()> abc_list_rule = -(abc_rule % ',') >> omit[*qi::space] >> qi::eoi;
    
    其中abc_rule % ','等价于abc_rule >> *(',' >> abc_rule),-表示整个列表是可选的,配合omit[*qi::space]可忽略全空格输入。
  • 方式2:手动构造规则,明确允许空:
    qi::rule<It, std::vector<Out>()> abc_list_rule = omit[*qi::space] >> -(abc_rule >> *(',' >> abc_rule)) >> omit[*qi::space] >> qi::eoi;
    

修改后的完整代码

#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

struct Out
{
    std::string a, b, c;
};
BOOST_FUSION_ADAPT_STRUCT( Out, a, b, c )

int main()
{
    using It = std::string_view::const_iterator;
    qi::rule<It, std::string()> identifier_rule = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" );

    #if 1
    qi::rule<It, Out()> abc_rule                     //
        = qi::omit[*qi::space] >> identifier_rule    //
          >> qi::omit[+qi::space] >> identifier_rule //
          >> qi::omit[+qi::space] >> identifier_rule //
          >> qi::omit[*qi::space];
    #else
    qi::rule<It, Out()> abc_rule = qi::skip( qi::space )[identifier_rule >> identifier_rule >> identifier_rule];
    #endif

    // 修改后的列表规则:支持空列表+强制解析到末尾
    qi::rule<It, std::vector<Out>()> abc_list_rule = -(abc_rule % ',') >> omit[*qi::space] >> qi::eoi;

    for( std::string_view test : {
             "a0 b0 c0,a1 b1 c1",            // valid
             "a0 b0 c0,",                    // 现在判定为无效
             "XXXX",                         // invalid
             "a0 b0 c0",                     // valid
             "a0",                           // invalid
             "   ",                          // 现在解析为空列表
             " , ",                          // invalid
         } )
    {
        std::cout << "Parsing: [" << test << "]\n";
        It f = test.begin(), l = test.end();
        std::vector<Out> v;
        if( qi::parse( f, l, abc_list_rule, v ) )
        {
            std::cout << "Parsed:\n";
            if( v.empty() )
                std::cout << "  Empty list\n";
            for( auto& o : v )
            {
                std::cout << "\nA: " << std::quoted( o.a ) << "\n"
                          << "B: " << std::quoted( o.b ) << "\n"
                          << "C: " << std::quoted( o.c ) << "\n";
            }
        }
        else
        {
            std::string left( f, l );
            std::cout << "Failed to parse " << std::quoted( test ) << " left: [" << left << "]\n";
        }
    }
}

内容的提问来源于stack exchange,提问作者llm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 17:06:01