如何使用boost::spirit解析csv
How to parse csv using boost::spirit
我有这个csv行
std::string s = R"(1997,Ford,E350,"ac, abs, moon","some "rusty" parts",3000.00)";
我可以使用boost::tokenizer
:解析它
typedef boost::tokenizer< boost::escaped_list_separator<char> , std::string::const_iterator, std::string> Tokenizer;
boost::escaped_list_separator<char> seps('', ',', '"');
Tokenizer tok(s, seps);
for (auto i : tok)
{
std::cout << i << std::endl;
}
它得到了正确的,除了令牌";生锈的";应该有被剥离的双引号:
some rusty parts
以下是我尝试使用助推::精神
boost::spirit::classic::rule<> list_csv_item = !(boost::spirit::classic::confix_p('"', *boost::spirit::classic::c_escape_ch_p, '"') | boost::spirit::classic::longest_d[boost::spirit::classic::real_p | boost::spirit::classic::int_p]);
std::vector<std::string> vec_item;
std::vector<std::string> vec_list;
boost::spirit::classic::rule<> list_csv = boost::spirit::classic::list_p(list_csv_item[boost::spirit::classic::push_back_a(vec_item)],',')[boost::spirit::classic::push_back_a(vec_list)];
boost::spirit::classic::parse_info<> result = parse(s.c_str(), list_csv);
if (result.hit)
{
for (auto i : vec_item)
{
cout << i << endl;
}
}
问题:
不起作用,只打印第一个令牌
为什么提升::精神::经典?找不到使用Spirit V2 的示例
设置太残忍了。。但我可以接受这个
**我真的很想使用boost::spirit
,因为它往往是相当快的
预期输出:
1997
Ford
E350
ac, abs, moon
some "rusty" parts
3000.00
有关解析(可选)引号分隔字段的背景信息,包括不同的引号字符(
'
、"
),请参阅此处:
- 使用boost::spirit分析带引号的字符串
对于非常、非常完整的示例,包括对部分引用的值和的支持
splitInto(input, output, ' ');
方法,该方法采用"任意"输出容器和分隔符表达式,请参见此处:
- 如何使我的拆分只在一个真正的行上工作,并能够跳过字符串的引用部分
解决您的确切问题,假设有引号的或未引号的字段(字段值内没有部分引号),使用Spirit V2:
让我们采用最简单的可能起作用的"抽象数据类型":
using Column = std::string;
using Columns = std::vector<Column>;
using CsvLine = Columns;
using CsvFile = std::vector<CsvLine>;
重复的双引号转义了双引号语义(正如我在评论中指出的),您应该能够使用以下内容:
static const char colsep = ',';
start = -line % eol;
line = column % colsep;
column = quoted | *~char_(colsep);
quoted = '"' >> *("""" | ~char_('"')) >> '"';
以下完整的测试程序打印
[1997][Ford][E350][ac, abs, moon][rusty][3001.00]
(请注意BOOstrongPIRIT_DEBUG的定义便于调试)。查看Coliru直播
完整演示
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
using Column = std::string;
using Columns = std::vector<Column>;
using CsvLine = Columns;
using CsvFile = std::vector<CsvLine>;
template <typename It>
struct CsvGrammar : qi::grammar<It, CsvFile(), qi::blank_type>
{
CsvGrammar() : CsvGrammar::base_type(start)
{
using namespace qi;
static const char colsep = ',';
start = -line % eol;
line = column % colsep;
column = quoted | *~char_(colsep);
quoted = '"' >> *("""" | ~char_('"')) >> '"';
BOOST_SPIRIT_DEBUG_NODES((start)(line)(column)(quoted));
}
private:
qi::rule<It, CsvFile(), qi::blank_type> start;
qi::rule<It, CsvLine(), qi::blank_type> line;
qi::rule<It, Column(), qi::blank_type> column;
qi::rule<It, std::string()> quoted;
};
int main()
{
const std::string s = R"(1997,Ford,E350,"ac, abs, moon","""rusty""",3001.00)";
auto f(begin(s)), l(end(s));
CsvGrammar<std::string::const_iterator> p;
CsvFile parsed;
bool ok = qi::phrase_parse(f,l,p,qi::blank,parsed);
if (ok)
{
for(auto& line : parsed) {
for(auto& col : line)
std::cout << '[' << col << ']';
std::cout << std::endl;
}
} else
{
std::cout << "Parse failedn";
}
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'n";
}
Sehe的帖子看起来比我的干净一点,但我把它放在一起有一段时间了,所以它在这里:
#include <boost/tokenizer.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
const std::string s = R"(1997,Ford,E350,"ac, abs, moon",""rusty"",3000.00)";
// Tokenizer
typedef boost::tokenizer< boost::escaped_list_separator<char> , std::string::const_iterator, std::string> Tokenizer;
boost::escaped_list_separator<char> seps('', ',', '"');
Tokenizer tok(s, seps);
for (auto i : tok)
std::cout << i << "n";
std::cout << "n";
// Boost Spirit Qi
qi::rule<std::string::const_iterator, std::string()> quoted_string = '"' >> *(qi::char_ - '"') >> '"';
qi::rule<std::string::const_iterator, std::string()> valid_characters = qi::char_ - '"' - ',';
qi::rule<std::string::const_iterator, std::string()> item = *(quoted_string | valid_characters );
qi::rule<std::string::const_iterator, std::vector<std::string>()> csv_parser = item % ',';
std::string::const_iterator s_begin = s.begin();
std::string::const_iterator s_end = s.end();
std::vector<std::string> result;
bool r = boost::spirit::qi::parse(s_begin, s_end, csv_parser, result);
assert(r == true);
assert(s_begin == s_end);
for (auto i : result)
std::cout << i << std::endl;
std::cout << "n";
}
这个输出:
1997
Ford
E350
ac, abs, moon
rusty
3000.00
1997
Ford
E350
ac, abs, moon
rusty
3000.00
值得注意的一点:这并没有实现完整的CSV解析器。您还需要研究转义符或实现所需的任何其他字符。
还有:如果您正在查看文档,请注意,在Qi中,'a'
等效于boost::spirit::qi::lit('a')
,"abc"
等效于boost::spirit::qi::lit("abc")
。
关于双引号:因此,正如Sehe在上面的评论中所指出的,目前还不清楚输入文本中围绕""
的规则意味着什么。如果您希望不在带引号的字符串中的""
的所有实例都转换为"
,那么下面这样的操作就可以了。
qi::rule<std::string::const_iterator, std::string()> double_quote_char = """" >> qi::attr('"');
qi::rule<std::string::const_iterator, std::string()> item = *(double_quote_char | quoted_string | valid_characters );
- 使用 istream 提取运算符进行 csv 解析:如何检测缺失的字段值?
- 获取线函数不会解析 CSV 文件中的行尾
- 解析具有提升精神的简单 csv 表
- Boost Tokenizer无法解析具有带有双引号的字段的CSV文件
- 如何使用 Boost 内存映射解析 C++ 中的 CSV?
- 在 C++ 中解析 csv 退出代码 11
- 在开关盒循环中使用未删除的标识符来解析 CSV 文件
- 如何使用没有清晰格式样式的C 解析CSV文件
- 解析 csv 文件,"malloc: *** error for object 0x7ffeeb4f4b80: pointer being freed was not allocated"出现此错误
- 解析一般CSV读取功能时,如何处理不同的数据类型?(不明确指定它们)
- 解析从 Excel 导出Microsoft csv 文件
- 具有多个 EOL 字符的常规 CSV 解析器
- 提升精神解析 CSV,列顺序可变
- 解析 csv 文件 c++
- 为游戏创建用户(读取和解析CSV文件)
- 如何使用boost::spirit解析csv
- 使用令牌解析csv文件中的特定列
- 通过c++解析csv
- ATL正则表达式解析csv文件
- 如何使用c++解析csv文件并对其进行算术运算