我正在尝试使用以下代码将 INI 文件信息存储在结构中:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <map>
#include <vector>
using Key = std::string;
using Value = std::string;
using Section = std::map<Key, Value>;
namespace qi = boost::spirit::qi;
namespace client
{
struct IniSection
{
std::string name;
Section pair;
};
struct IniFile
{
std::vector<IniSection> sections;
};
};
namespace client
{
template <typename Iterator>
struct ini_grammar : qi::grammar<Iterator, IniFile()>
{
ini_grammar() : ini_grammar::base_type(start)
{
skipper = qi::blank | '#' >> *(qi::char_ - qi::eol);
key = +qi::char_("a-zA-Z_0-9");
value = *(qi::char_ - qi::eol);
pair = key >> '=' >> value;
section = '[' >> key >> ']' >> +qi::eol >> *(pair >> +qi::eol);
file = *section;
start = qi::skip(copy(skipper))[file];
}
using Skipper = qi::rule<Iterator>;
using KVP = std::pair<Key, Value>;
Skipper skipper;
qi::rule<Iterator, IniFile()> start;
qi::rule<Iterator, IniFile(), Skipper> file;
qi::rule<Iterator, IniSection()> section;
qi::rule<Iterator, KVP()> pair;
qi::rule<Iterator> value;
qi::rule<Iterator> key;
};
}
int main()
{
std::string const ini_section =
R"([Section]
key1 = value1
key2 = value2
)";
using It = std::string::const_iterator;
client::ini_grammar<It> grammar;
client::IniFile iniFile;
It iter = ini_section.begin(), end = ini_section.end();
bool r = parse(iter, end, grammar, iniFile);
if (iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
for (const auto& section : iniFile.sections)
{
std::cout << "[" << section.name << "]\n";
for (const auto& kvp : section.pair)
{
std::cout << kvp.first << " = " << kvp.second << "\n";
}
}
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "Stopped at position: " << std::distance(ini_section.begin(), iter) << std::endl;
std::cout << "\nRemaining input unparsed:\n" << std::string(iter, end);
std::cout << "-------------------------\n";
}
}
问题是代码输出了一个巨大的错误,而我什么也不明白。我认为大部分错误与解决问题无关,因此我将给出我认为重要的部分。
boost_1_84_0/boost/spirit/home/support/container.hpp:130:12: error: no type named ‘value_type’ in ‘struct client::IniFile’
130 | struct container_value
| ^~~~~~~~~~~~~~~
boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:320:66: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniFile, void>’
320 | typedef typename traits::container_value<Attr>::type value_type;
| ^~~~~~~~~~
boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:333:15: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniFile, void>’
333 | > predicate;
| ^~~~~~~~~
boost_1_84_0/boost/spirit/home/support/container.hpp:130:12: error: no type named ‘value_type’ in ‘struct client::IniSection’
130 | struct container_value
| ^~~~~~~~~~~~~~~
boost/boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:320:66: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniSection, void>’
320 | typedef typename traits::container_value<Attr>::type value_type;
|
boost/boost_1_84_0/boost/spirit/home/qi/detail/pass_container.hpp:333:15: error: no type named ‘type’ in ‘struct boost::spirit::traits::container_value<client::IniSection, void>’
333 | > predicate;
| ^~~~~~~~~
我使用此问题的答案中提供的代码作为参考。我尝试使用
BOOST_FUSION_ADAPT_STRUCT
来解决该问题,但我认为这与问题无关,它只是造成了更多错误。
看起来自从上次以来你一直在改变事情。一些转变引起了混乱。
例如:
using Section = std::map<Key, Value>;
struct IniSection {
std::string name;
Section pair;
};
成员名称
pair
与其类型 Section
之间存在严重脱节。
现在我将 100% 使用像我之前展示的关联容器,而不是
std::vector<IniSection>
:
using IniFile = std::multimap<Key, Section>;
但是我们暂时假设您确实想保留部分的输入顺序(显然不是键)。逻辑上的改变是
using IniFile = std::pair<Key, Section>;
引入你自己的类型
IniSection
和IniFile
意味着你必须教Qi如何向它们传播属性。我可以向您展示这一点,但首先让我们保持简单:
namespace client {
using Key = std::string;
using Value = std::string;
using Entries = std::map<Key, Value>;
using Section = std::pair<Key, Entries>;
using IniFile = std::vector<Section>;
}; // namespace client
但是,不知何故,你的
value
和 key
规则已经放弃了它们的属性。这很简单意味着他们根本无法暴露自己的属性:
qi::rule<Iterator> value;
qi::rule<Iterator> key;
因此不会再发生自动传播,因为没有任何东西可以传播。根据定义,所有数据结构都与“无属性”不兼容。首先从我之前的回答中恢复这些:
// lexemes
qi::rule<Iterator, Key()> key;
qi::rule<Iterator, Value()> value;
现在你包括
#include <boost/fusion/include/adapt_struct.hpp>
这也意味着您停止了包括
#include <boost/fusion/include/std_pair.hpp>
我们也从我之前的回答中恢复一下:
#include <boost/fusion/adapted.hpp> // includes both
此时再次编译,但它不再解析整个部分。仔细观察,那是因为您将船长从
IniSection
和 KVP
规则中删除了。 (为什么?)
qi::rule<Iterator, IniSection()> section;
qi::rule<Iterator, KVP()> pair;
从我之前的答案中恢复,我们得到了完整的解析:
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <map>
namespace client {
using Key = std::string;
using Value = std::string;
using Entries = std::map<Key, Value>;
using Section = std::pair<Key, Entries>;
using IniFile = std::vector<Section>;
}; // namespace client
namespace client {
namespace qi = boost::spirit::qi;
template <typename Iterator> struct ini_grammar : qi::grammar<Iterator, IniFile()> {
ini_grammar() : ini_grammar::base_type(start) {
skipper = qi::blank | '#' >> *(qi::char_ - qi::eol);
key = +qi::char_("a-zA-Z_0-9");
value = *(qi::char_ - qi::eol);
entry = key >> '=' >> value;
section = '[' >> key >> ']' >> +qi::eol >> *(entry >> +qi::eol);
file = *section;
start = qi::skip(copy(skipper))[file];
}
using Skipper = qi::rule<Iterator>;
using Entry = std::pair<Key, Value>;
Skipper skipper;
qi::rule<Iterator, IniFile()> start;
qi::rule<Iterator, IniFile(), Skipper> file;
qi::rule<Iterator, Section(), Skipper> section;
qi::rule<Iterator, Entry(), Skipper> entry;
qi::rule<Iterator, Key()> value;
qi::rule<Iterator, Value()> key;
};
} // namespace client
int main() {
for (std::string const input : {
R"([Section]
key1 = value1
key2 = value2
[Section10]
key3 = value3
[Section3] # let's see that order is preserved
key4 = value4
)",
}) {
std::cout << "-------------------------\n";
using It = std::string::const_iterator;
client::ini_grammar<It> grammar;
client::IniFile iniFile;
It iter = input.begin(), end = input.end();
bool r = parse(iter, end, grammar, iniFile);
if (r) {
std::cout << "Parsing succeeded\n";
for (auto const& [name, entries] : iniFile) {
std::cout << "[" << name << "]\n";
for (auto const& [k,v] : entries) {
std::cout << k << " = " << v << "\n";
}
}
} else {
std::cout << "Parsing failed\n";
}
if (iter!=end) {
std::cout << "Stopped at position: " << std::distance(input.begin(), iter) << std::endl;
std::cout << "\nRemaining input unparsed:\n" << std::string(iter, end);
}
}
}
印刷
-------------------------
Parsing succeeded
[Section]
key1 = value1
key2 = value2
[Section10]
key3 = value3
[Section3]
key4 = value4
您甚至不再检查 main 中的解析结果(
r
)。您也不会将 检查 eoi
合并到解析器中。添加最后的润色:
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <map>
namespace client {
using Key = std::string;
using Value = std::string;
using Entries = std::map<Key, Value>;
using Section = std::pair<Key, Entries>;
using IniFile = std::vector<Section>;
}; // namespace client
namespace client {
namespace qi = boost::spirit::qi;
template <typename Iterator> struct ini_grammar : qi::grammar<Iterator, IniFile()> {
ini_grammar() : ini_grammar::base_type(start) {
skipper = qi::blank | '#' >> *(qi::char_ - qi::eol);
key = +qi::char_("a-zA-Z_0-9");
value = *(qi::char_ - qi::eol);
entry = key >> '=' >> value >> (+qi::eol | qi::eoi);
section = '[' >> key >> ']' >> (+qi::eol | qi::eoi) >> *entry;
file = *section;
start = qi::skip(copy(skipper))[ //
file >> qi::eoi //
];
}
private:
using Skipper = qi::rule<Iterator>;
using Entry = std::pair<Key, Value>;
Skipper skipper;
qi::rule<Iterator, IniFile()> start;
qi::rule<Iterator, IniFile(), Skipper> file;
qi::rule<Iterator, Section(), Skipper> section;
qi::rule<Iterator, Entry(), Skipper> entry;
qi::rule<Iterator, Key()> value;
qi::rule<Iterator, Value()> key;
};
} // namespace client
int main() {
using It = std::string::const_iterator;
static client::ini_grammar<It> const grammar;
for (std::string const input :
{
R"([Section]
key1 = value1
key2 = value2
[Section10]
key3 = value3
[Section3] # let's see that order is preserved
key4 = value4
)",
R"()",
R"(oops=bad)",
}) //
{
std::cout << "-------------------------\n";
client::IniFile iniFile;
if (parse(input.begin(), input.end(), grammar, iniFile)) {
std::cout << "Parsing succeeded\n";
for (auto const& [name, entries] : iniFile) {
std::cout << "[" << name << "]\n";
for (auto const& [k,v] : entries) {
std::cout << k << " = " << v << "\n";
}
}
} else {
std::cout << "Parsing failed\n";
}
}
}
打印预期输出
-------------------------
Parsing succeeded
[Section]
key1 = value1
key2 = value2
[Section10]
key3 = value3
[Section3]
key4 = value4
-------------------------
Parsing succeeded
-------------------------
Parsing failed
您很好地修改了代码以满足您的需求,并“使其成为您自己的”,即完全理解它。然而,一路走来,你已经积累了许多干扰性变化的组合,这些变化一起使你自己无法看到哪里出了问题。我有一个怀疑,一件事导致了另一件事,你就在这里。
教训是:无论如何都要去调整代码。如果程序员害怕接触代码,他们就无法正常工作。但是,请遵循两个基本原则帮助自己:
这意味着您“永远不会”发生无法编译的更改(您只需撤消该更改),并且当您破坏某些内容时,您将“立即”发现。这并不一定意味着您必须撤消更改。也许您需要补偿代码中其他地方的更改。无论如何,在所有测试再次通过之前,不要更改任何不相关的内容。 这就是开明程序员之道。
¹也许您开始接受副驾驶的一些建议?