c++ - 如何使用 Boost Spirit 解析类似转义字符串的 CSV?
问题描述
对于我的快速解析器项目,我想使用 CSV like escaping: ""
to escape"
例子:
"\"hello\"",
" \" hello \" ",
" \" hello \"\"stranger\"\" \" ",
在线编译尝试:https ://wandbox.org/permlink/5uchQM8guIN1k7aR
我当前的解析规则只解析前 2 个测试
qi::rule<std::string::const_iterator, qi::blank_type, utree()> double_quoted_string
= '"' >> qi::no_skip[+~qi::char_('"')] >> '"';
我发现了这个 stackoverflow 问题,并且使用精神给出了一个答案:
start = field % ',';
field = escaped | non_escaped;
escaped = lexeme['"' >> *( char_ -(char_('"') | ',') | COMMA | DDQUOTE) >> '"'];
non_escaped = lexeme[ *( char_ -(char_('"') | ',') ) ];
DDQUOTE = lit("\"\"") [_val = '"'];
COMMA = lit(",") [_val = ','];
(我不知道如何链接答案,所以如果有兴趣搜索“当你使用像 boost::spirit 这样美丽的东西时,你会感到自豪”)
遗憾的是,它并没有为我编译 - 甚至多年的 C++ 错误消息分析也没有让我为精神错误消息泛滥做好准备:) 如果我理解它正确,规则将等待,
作为字符串分隔符,什么可能不正确我的表达式解析器项目的东西
expression = "strlen( \"hello \"\"you\"\" \" )+1";
expression = "\"hello \"";
expression = "strlen(concat(\"hello\",\"you\")+3";
或者在这种情况下,规则是否需要可选地等待,
和)
?
我希望我不要问太多愚蠢的问题,但答案有助于我进入精神表达式解析本身几乎可以工作,除了字符串转义
谢谢任何帮助
更新:这似乎对我有用,至少它解析字符串但从字符串中删除了转义"
,是否有更好的调试输出可用于字符串?" " " " "h" "e" "l" "l" "o" " " "s" "t" "r" "a" "n" "g" "e" "r" " "
不是真的那么可读
qi::rule<std::string::const_iterator, utree()> double_quoted_string
= qi::lexeme['"' >> *(qi::char_ - (qi::char_('"')) | qi::lit("\"\"")) >> '"'];
解决方案
您可以将问题简化为这一点。如何使双引号字符串接受“双双引号”以转义嵌入的双引号字符?
一个没有转义的简单字符串解析器:
qi::rule<It, std::string()> s = '"' >> *~qi::char_('"') >> '"';
现在,要根据需要也接受单个转义"
,只需添加:
s = '"' >> *("\"\"" >> qi::attr('"') | ~qi::char_('"')) >> '"';
其他注意事项:
- 在您的在线示例中,使用
no_skip
是草率的:它会解析"foo bar"
和" foo bar "
(foo bar
修剪空格)。相反,从规则中删除船长以使其隐含地词位(再次)。 - 您的解析器不接受空字符串(这可能是您想要的,但这不确定)
- 使用 utree 可能会使您的生活变得比您想要的更复杂
简化:
#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <iomanip>
#include <string>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace fu = boost::fusion;
int main()
{
auto tests = std::vector<std::string>{
R"( "hello" )",
R"( " hello " )",
R"( " hello ""escaped"" " )",
};
for (const std::string& str : tests) {
auto iter = str.begin(), end = str.end();
qi::rule<std::string::const_iterator, std::string()> double_quoted_string
= '"' >> *("\"\"" >> qi::attr('"') | ~qi::char_('"')) >> '"';
std::string ut;
bool r = qi::phrase_parse(iter, end, double_quoted_string >> qi::eoi, qi::blank, ut);
std::cout << str << " ";
if (r) {
std::cout << "OK: " << std::quoted(ut, '\'') << "\n";
}
else {
std::cout << "Failed\n";
}
if (iter != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end)) << "\n";
}
std::cout << "----\n";
}
}
印刷
"hello" OK: 'hello'
----
" hello " OK: ' hello '
----
" hello ""escaped"" " OK: ' hello "escaped" '
----
推荐阅读
- javascript - 使用 Javascript 构建 JSON 文件
- reactjs - 在 React 中为机器人和屏幕阅读器显示初始元素
- css - 如何将动态文本的按钮宽度设置为 div 内最宽的按钮宽度?
- ms-word - 如何使用 Word Javascript API 以编程方式将 ^13 替换为 ^p?
- javascript - 使用 mapbox 在地图中添加 geojson 文件并创建多边形
- python - TensorFlow 去除 JFIF
- c++ - 如何访问指针结构的成员变量?
- puppeteer - Puppeteer:在 browser.disconnect 之后,Chromium 实例在后台保持活动状态
- powershell - PowerShell中的`>`(重定向)和Out-File有什么区别
- python - Google Cloud Profiler 显示 [未知 - 无 Python 线程状态]