使用 boost::spirit 以任意顺序解析命名参数

2023-12-09

我正在为一种输入文件编写一个解析器。输入文件看起来像:

[CalculationBlock]
CalculationTitle="Test Parser Input System" , MatchingRadius=25.0, StepSize=0.01,ProblemType=RelSchroedingerEqn
MaxPartialWaveJ=800, SMatConv=10E-8
PartialWaveConv= 10E-8, SmallValueLimit = 10E-8
PotentialRadType=HeavyIon
[end]

本质上它被分为以以下开头的块[BlockName]然后里面有一组命名参数。命名参数可以用以下分隔符分隔',' or '\n'人物。

使用上面给出的不完整输入文件,我想为其编写一个解析器,作为更完整输入文件的起点。我这样做了但是解析器有一个弱点,我不知道如何解决。它与参数顺序无关。例如,如果用户要输入参数PartialWaveConv= 10E-8 before SMatConv=10E-8它会失败。

我曾短暂考虑过枚举块中参数的每个可能顺序,但我放弃了它,因为有n!n 个参数值对的排列。所以我的问题是:有什么方法可以使解析器独立于参数排序吗?

我写的玩具解析器如下,如果它是业余的,我很抱歉,这是我第一次涉足boost,更不用说boost.spirit.

#include<string>
#include<iostream>
#include<cstdlib>
#include<fstream>
#include<boost/config/warning_disable.hpp>
#include<boost/spirit/include/qi.hpp>
#include<boost/spirit/include/phoenix_core.hpp>
#include<boost/spirit/include/phoenix_operator.hpp>
#include<boost/spirit/include/phoenix_object.hpp>
#include<boost/fusion/include/adapt_struct.hpp>
#include<boost/fusion/include/io.hpp>
#include<boost/spirit/include/support_istream_iterator.hpp>

namespace blocks
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

struct CalcBlock
{
    std::string calculationTitle;
    float matchingRad;
    float stepSize;
    std::string problemType;
    int maxPartialWaveJ;
    float sMatrixConvergenceValue;
    float partialWaveConvergenceValue;
    float smallValueLimit;
    std::string potentialRadType;
};

}

//tell fusion about the block structure
BOOST_FUSION_ADAPT_STRUCT(blocks::CalcBlock,
                        (std::string, calculationTitle)
                        (float, matchingRad)
                        (float, stepSize)
                        (std::string, problemType)
                        (int, maxPartialWaveJ)
                        (float, sMatrixConvergenceValue)
                        (float, partialWaveConvergenceValue)
                        (float, smallValueLimit)
                        (std::string, potentialRadType)
)

namespace blocks
{

template <typename Iterator>
struct CalcBlockParser : qi::grammar<Iterator, CalcBlock(), boost::spirit::ascii::blank_type>
{
    CalcBlockParser() : CalcBlockParser::base_type(start)
    {
        using qi::int_;
        using qi::lit;
        using qi::float_;
        using qi::lexeme;
        using ascii::char_;

        quotedString %= lexeme['"' >> +(char_ - '"' - '\n') >> '"'];
        plainString %= lexeme[ +(char_ - ' ' - ',' - '\n') ];

        start %=
            lit("[CalculationBlock]") >> '\n'
            >> lit("CalculationTitle") >> '=' >> quotedString >> (lit(',') | lit('\n'))
            >> lit("MatchingRadius") >> '=' >> float_ >> (lit(',') | lit('\n'))
            >> lit("StepSize") >> '=' >> float_ >> (lit(',') | lit('\n'))
            >> lit("ProblemType") >> '=' >> plainString >> (lit(',') | lit('\n'))
            >> lit("MaxPartialWaveJ") >> '=' >> int_ >> (lit(',') | lit('\n'))
            >> lit("SMatConv") >> '=' >> float_ >> (lit(',') | lit('\n'))
            >> lit("PartialWaveConv") >> '=' >> float_ >> (lit(',') | lit('\n'))
            >> lit("SmallValueLimit") >> '=' >> float_ >> (lit(',') | lit('\n'))
            >> lit("PotentialRadType") >> '=' >> plainString
            >> lit("\n[end]\n");
    }

    qi::rule<Iterator, std::string(), boost::spirit::ascii::blank_type> quotedString;
    qi::rule<Iterator, std::string(), boost::spirit::ascii::blank_type> plainString;
    qi::rule<Iterator, CalcBlock(), boost::spirit::ascii::blank_type> start;
};

}

using std::cout;
using std::endl;
namespace spirit = boost::spirit;
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << "\nUsage:\n\t./echos InputFileName\n" << endl;
        return EXIT_FAILURE;
    }

    std::string inputFileName(argv[1]);
    cout << "Reading input from the file: " << inputFileName << endl;
    std::ifstream input(inputFileName);
    input.unsetf(std::ios::skipws);

    spirit::istream_iterator start(input);
    spirit::istream_iterator stop;

    typedef blocks::CalcBlockParser<spirit::istream_iterator> CalcBlockParser;

    CalcBlockParser cbParser;

    blocks::CalcBlock cb;

    bool success = phrase_parse(start, stop, cbParser, boost::spirit::ascii::blank, cb);

    if (success && start == stop)
    {
        std::cout << boost::fusion::tuple_open('[');
        std::cout << boost::fusion::tuple_close(']');
        std::cout << boost::fusion::tuple_delimiter(", ");

        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "got: " << boost::fusion::as_vector(cb) << std::endl;
        std::cout << "\n-------------------------\n";
    }
    else
    {
        std::cout << boost::fusion::tuple_open('[');
        std::cout << boost::fusion::tuple_close(']');
        std::cout << boost::fusion::tuple_delimiter(", ");

        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "got: " << boost::fusion::as_vector(cb) << std::endl;
        std::cout << "\n-------------------------\n";
    }

    return EXIT_SUCCESS;
}

只是为了好玩/完整性,我复习了语法并提出了以下测试。

我左右提出了一些改进建议(正如OP在直播中见证的那样),生成的代码、测试和输出如下:

Live On Coliru

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
#include <iostream>

namespace blocks {
    struct CalcBlock {
        std::string calculationTitle;
        float       matchingRad;
        float       stepSize;
        std::string problemType;
        int         maxPartialWaveJ;
        float       sMatrixConvergenceValue;
        float       partialWaveConvergenceValue;    
        float       smallValueLimit;
        std::string potentialRadType;
    };
}

BOOST_FUSION_ADAPT_STRUCT(blocks::CalcBlock, // Boost 1.58+ style adapt-struct
        calculationTitle, matchingRad, stepSize, problemType, maxPartialWaveJ,
        sMatrixConvergenceValue, partialWaveConvergenceValue, smallValueLimit,
        potentialRadType)

namespace blocks {

    namespace qi = boost::spirit::qi;

    template <typename Iterator>
    struct CalcBlockParser : qi::grammar<Iterator, CalcBlock()> {

        CalcBlockParser() : CalcBlockParser::base_type(start) {

            using namespace qi;
            auto eol_ = copy((',' >> *eol) | +eol); // http://stackoverflow.com/a/26411266/85371 (!)

            quotedString = '"' >> +~char_("\"\n") >> '"';
            plainString  =  +~char_(" ,\n");

            start        = skip(blank) [cbRule];

            cbRule       = lexeme["[CalculationBlock]"] >> eol 
              >> (
                      (lexeme["CalculationTitle"] >> '=' >> quotedString >> eol_)
                    ^ (lexeme["MatchingRadius"]   >> '=' >> float_       >> eol_)
                    ^ (lexeme["StepSize"]         >> '=' >> float_       >> eol_)
                    ^ (lexeme["ProblemType"]      >> '=' >> plainString  >> eol_)
                    ^ (lexeme["MaxPartialWaveJ"]  >> '=' >> int_         >> eol_)
                    ^ (lexeme["SMatConv"]         >> '=' >> float_       >> eol_)
                    ^ (lexeme["PartialWaveConv"]  >> '=' >> float_       >> eol_)
                    ^ (lexeme["SmallValueLimit"]  >> '=' >> float_       >> eol_)
                    ^ (lexeme["PotentialRadType"] >> '=' >> plainString  >> eol_)
                 )
             >> lexeme["[end]"]
             >> *eol 
             >> eoi;
        }

      private:
        qi::rule<Iterator, CalcBlock()> start;
        qi::rule<Iterator, CalcBlock(), qi::blank_type> cbRule;
        // lexemes:
        qi::rule<Iterator, std::string()> quotedString, plainString;
    };
}

using   boost::fusion::as_vector;
typedef boost::spirit::istream_iterator It;

int main(int argc, char **argv) {
    if (argc != 2) {
        std::cout << "Usage:\n\t" << argv[0] << " InputFileName" << std::endl;
        return 1;
    }

    std::string inputFileName(argv[1]);
    std::cout << "Reading input from the file: " << inputFileName << std::endl;
    std::ifstream input(inputFileName);
    input.unsetf(std::ios::skipws);

    It start(input), stop;

    blocks::CalcBlock cb;
    blocks::CalcBlockParser<It> cbParser;

    bool success = parse(start, stop, cbParser, cb);

    {
        using namespace boost::fusion;
        std::cout << tuple_open('[') << tuple_close(']') << tuple_delimiter(", ");
    }

    std::cout << "-------------------------\n";
    std::cout << "Parsing " << (success?"succeeded":"failed") << "\n";
    std::cout << "got: "    << as_vector(cb)                  << "\n";
    std::cout << "-------------------------\n";
}

Input:

[CalculationBlock]
CalculationTitle="Test Parser Input System"


SMatConv=10E-8,


PartialWaveConv= 10E-8, MaxPartialWaveJ=800, SmallValueLimit = 10E-8

PotentialRadType=HeavyIon , MatchingRadius=25.0, StepSize=0.01,ProblemType=RelSchroedingerEqn

[end]

Output:

Reading input from the file: input.txt
-------------------------
Parsing succeeded
got: [Test Parser Input System, 25, 0.01, RelSchroedingerEqn, 800, 1e-07, 1e-07, 1e-07, HeavyIon]
-------------------------
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 boost::spirit 以任意顺序解析命名参数 的相关文章

  • 如何使用 C# 中的参数将用户重定向到 paypal

    如果我有像下面这样的简单表格 我可以用它来将用户重定向到 PayPal 以完成付款
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • C++ 标准是否指定了编译器的 STL 实现细节?

    在写答案时this https stackoverflow com questions 30909296 can you put a pimpl class inside a vector我遇到了一个有趣的情况 这个问题演示了这样一种情况

随机推荐

  • 单行无限分离循环的语法

    我可以像这样无限循环地运行一些东西 while true do foo done 我可以像这样运行一些独立的东西 foo 但我不能像这样在无限循环中运行分离的东西 while true do foo done bash syntax err
  • 通过远程 java 独立应用程序使用 EJB 3.1 bean

    我一直在尝试使用 Java EE 6 创建一个基于应用程序服务器的应用程序 该应用程序从 GWT Web 应用程序接收作业对象 并且这些作业将从 Java 独立应用程序中提取 我一直认为 EJB 模型将为我提供一种简单的远程处理方法 因为我
  • 如何在 Spring-Batch 中使用 ItemReader 跳行?

    我有一个自定义项目阅读器 可以将文本文件中的行转换为我的实体 public class EntityItemReader extends AbstractItemStreamItemReader
  • RegEx 字符串,12 个符号(至少 1 个数字和至少 1 个字母)

    我在使用正则表达式时遇到问题 如何获取至少包含 1 个数字和 1 个字母的字符串的 12 个符号长部分 例子 这有 12 个符号长 F8ENL83I0E12也许还有更多文字 要在较长的文本中查找长度为 12 的字母数字单词 请使用 i Ca
  • 如何快速编写这段代码?

    我用 Objective c 编写了这段代码 NSRect textRect NSMakeRect 42 35 117 55 NSString textContent Hello World NSMutableParagraphStyle
  • AVAudioPlayer 在模拟器上工作但在真实设备上不工作

    当我播放录制的音频时 出现此错误 致命错误 在解包可选值时意外发现 nil 在这行代码上 SoundPlayer try AVAudioPlayer contentsOfURL getFileURL 但它在除真实设备之外的模拟器上运行良好
  • Rails 表单提交与远程 => true -- js 文件渲染但不执行

    有一个类似的问题here and here但都没有我正在寻找的答案 我也做了很多搜索 rails format js render 但无法解决这个问题 在 Rails 4 中 我有一个经过验证的表单 如下所示 don t want to c
  • 模拟 ScheduledExecutorService 与“不要模拟您不拥有的类型”哲学

    Mocking 预定执行服务确实会让测试我的课程变得更容易 但根据模拟推荐这似乎是一个坏主意 因为模拟类的逻辑可能会以错误的方式使用 但单元测试仍然会报告成功 似乎为它编写一个包装器是 干净 的方式 但我有一种感觉 这只会导致接口的完全重复
  • Python3 在同一行上打印 - 7 段设备格式的数字

    我是 Python 新手 很难将输出打印在一行上 这与在线 Python 课程 Learning Python Essentials Lab 5 1 10 6 和打印到 7 段设备有关 如果您不熟悉 7 段设备 请参阅维基百科 我没有使用任
  • 是否可以将搜索过滤器/框添加到树视图组件中?

    我正在制作扩展 我很好奇是否可以向树组件添加搜索过滤器 框 我目前正在用 javascript 编码扩展 但我不确定它是否可能 这就是我想在树视图顶部添加的内容 它将在 v1 70 中内置到 vscode 中 它在我为垂直选项卡组编写的树视
  • 使用 Linq 表达式和反射获取属性值的通用方法

    亲爱的反思之神 我想要一个通用的GetValue
  • 正则表达式 match() 无法捕获 python 中的简单模式

    我正在尝试在 Python 中使用一些简单的正则表达式函数 我正在使用正则表达式来捕获阿拉伯字母表中的模式 但在最简单的情况下 当人们在模式的开头添加几个字母时 无论是否有连字 它似乎都不起作用 gt gt gt p re compile
  • 等待几秒钟而不阻止 UI 执行

    我想在两条指令之间等待几秒钟 但不阻止执行 例如 Thread Sleep 2000 这不好 因为它会阻塞执行 我的想法是 我调用一个方法 然后等待 X 秒 例如 20 秒 监听即将到来的事件 在 20 秒结束时 我应该根据 20 秒内发生
  • C#.NET 使用 Windows 服务获取计算机的用户名

    我很难获取使用 Windows 服务登录计算机的人的用户名 当同时使用 System Environment UserName 或 WindowsIdentity GetCurrent UserName 时 我得到 NTAUTHORITY
  • 了解Android 6权限方法

    我试图从图库中获取图像并将其设置为图像视图 但在 Android 6 中存在一些权限问题 以下是请求许可的方法 我应该要求读取外部存储还是写入外部存储 这是我到目前为止所做的 private static final int READ CO
  • 如何使用 tastypie 登录 django

    我试图在自定义身份验证中覆盖 is authenticated 我有一些简单的事情 首先 是这样的 class MyAuthentication BasicAuthentication def init self args kwargs s
  • md-icons 如何在浏览器上呈现

    我在我的项目中使用了material2和Material图标 我想知道这些命名图标是如何在浏览器中呈现的 我用过
  • 电子重建内发生未处理的错误

    我正在尝试使用 Sqlite3 重建我的 ElectronJS 应用程序 我已经安装了 Python VS 开发工具 但它仍然给我错误 我的 Package json name hello version 1 0 0 description
  • django中如何区分首次注册用户和常规登录用户

    我所有的工作都使用 django allauthsignin signup and logout功能和工作正常 现在我有一个功能 1 当用户第一次注册并登录时 我需要将他重定向到成功页面 验证页面 success 2 当已经注册的用户登录时
  • 使用 boost::spirit 以任意顺序解析命名参数

    我正在为一种输入文件编写一个解析器 输入文件看起来像 CalculationBlock CalculationTitle Test Parser Input System MatchingRadius 25 0 StepSize 0 01