如何使用Boost解析ini文件

68

我有一个ini文件,其中包含一些示例值,例如:

[Section1]
Value1 = 10
Value2 = a_text_string
我在使用 Boost 尝试加载这些值,并在我的应用程序中打印它们,但我不理解如何在 C++ 中实现这一点。
我在这个论坛中搜索了一些例子(我之前一直使用 C, 所以我对 C++ 不是很熟悉),但我只找到了关于如何一次性从文件中读取所有值的示例。
我需要仅在需要时加载单个值,例如 string = Section1.Value2,因为我不需要读取所有的值,而只需要其中的一些。
我想要加载单个值并将其存储在变量中,以便在需要时在我的应用程序中使用它们。
使用 Boost 可以实现吗?
目前,我正在使用以下代码:
#include <iostream>
#include <string>
#include <set>
#include <sstream>
#include <exception>
#include <fstream>
#include <boost/config.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>

namespace pod = boost::program_options::detail;

int main()
{
   std::ifstream s("file.ini");
    if(!s)
    {
        std::cerr<<"error"<<std::endl;
        return 1;
    }

    std::set<std::string> options;
    options.insert("Test.a");
    options.insert("Test.b");
    options.insert("Test.c");

    for (boost::program_options::detail::config_file_iterator i(s, options), e ; i != e; ++i)
        std::cout << i->value[0] << std::endl;
   }

但是这只是在for循环中读取所有的值;相反,我只想在需要时读取单个值,并且我不需要将值插入文件中,因为文件已经写入了我程序中所需的所有值。

4个回答

152

你也可以使用Boost.PropertyTree读取.ini文件:

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>

...

boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini("config.ini", pt);
std::cout << pt.get<std::string>("Section1.Value1") << std::endl;
std::cout << pt.get<std::string>("Section1.Value2") << std::endl;

1
如果我不知道某些可用键的集合,但仍想获取键值对的完整列表怎么办?也就是说,如果不使用“Section1.Value1”和“Section1.Value2”常量,您将如何重写程序? - Oleg Vazhnev
更新:我已经找到了这个问题https://dev59.com/o2XWa4cB1Zd3GeqPJA7A 根据它的说法,如果没有编写自定义代码是不可能的... - Oleg Vazhnev
2
@javapowered,你找到的问题是关于Boost.Program_options,而不是关于Boost.PropertyTree。 - user
2
这里已经解决了:https://dev59.com/d3HYa4cB1Zd3GeqPIzht - user
我一直对Boost.PropertyTree的问题是在节名和值名之间使用“.”。真正的INI文件支持包含“.”字符的节名和值名。更好的分隔符应该是/或\。甚至可以使用非可打印字符,如响铃字符(\007)。是否可以自定义此行为? - Benilda Key
显示剩余2条评论

3

由于INI文件的简单结构,解析它们很容易。使用AXE,我可以用几行代码来解析部分、属性和注释:

auto trailing_spaces = *space & endl;
auto section = '[' & r_alnumstr() & ']';
auto name = +(r_any() - '=' - endl - space);
auto value = '"' & *("\\\"" | r_any() - '"') & '"'
   | *(r_any() - trailing_spaces);
auto property = *space & name & *space & '=' & *space 
    & value & trailing_spaces;
auto comment = ';' & *(r_any() - endl) & endl;
auto ini_file = *comment & *(section & *(prop_line | comment)) & r_end();

更详细的示例可以在Reference.pdf中找到。

关于不读取整个文件,可以用不同的方法来实现。首先,INI格式的解析器至少需要前向迭代器,因此不能使用流迭代器,因为它们是输入迭代器。你可以创建一个带有所需迭代器的流的单独类(我过去写过一个带有滑动缓冲区的类)。你可以使用内存映射文件。或者你可以使用动态缓冲区,从标准流中读取并提供给解析器,直到找到值。如果你不想要一个真正的解析器,并且不关心INI文件结构是否正确,你可以简单地在文件中搜索你的令牌。输入迭代器就足够了。

最后,我不确定避免读取整个文件是否带来任何优势。INI文件通常非常小,由于硬盘和多重缓冲系统会读取一个或多个扇区(即使你只需要一个字节),因此我怀疑通过尝试部分读取文件(特别是反复这样做)可能不会带来任何性能改进,相反可能会适得其反。


这是关于编程的内容,涉及到一个看起来非常有用的C++11解析库的参考链接:http://www.gbresearch.com/axe/。 - Arto Bendiken
迁移到Github - ddevienne

2

需要解析该文件,必须按顺序完成。因此,我将读取整个文件,将所有值存储在某个集合中(mapunordered_map,可能使用 pair<section, key> 作为键或使用映射的映射),并在需要时从集合中获取。


2

虽然使用了“经典”风格的Spirit库,但这是相当古老的。尽管如此,还是要加一分。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接