【问题标题】:iterate over ini file on c++, probably using boost::property_tree::ptree?迭代 c++ 上的 ini 文件,可能使用 boost::property_tree::ptree?
【发布时间】:2013-04-14 15:46:01
【问题描述】:

我的任务很简单——我只需要解析这样的文件:

Apple = 1
Orange = 2
XYZ = 3950

但我不知道可用键的集合。我用 C# 解析这个文件比较容易,让我演示一下源代码:

    public static Dictionary<string, string> ReadParametersFromFile(string path)
    {
        string[] linesDirty = File.ReadAllLines(path);
        string[] lines = linesDirty.Where(
            str => !String.IsNullOrWhiteSpace(str) && !str.StartsWith("//")).ToArray();

        var dict = lines.Select(s => s.Split(new char[] { '=' }))
                        .ToDictionary(s => s[0].Trim(), s => s[1].Trim());
        return dict;
    }

现在我只需要使用 c++ 做同样的事情。我正在考虑使用boost::property_tree::ptree,但似乎我无法遍历 ini 文件。易读ini文件:

boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini(path, pt);

但是不能迭代,参考这个问题Boost program options - get all entries in section

问题是 - 在 C++ 上编写上述 C# 代码模拟的最简单方法是什么?

【问题讨论】:

  • 当然可以迭代 ptree,事实上它是微不足道的,正如我现在的答案所示(更新)(注意:program-options != property-tree )

标签: c++ boost


【解决方案1】:

目前,我已经稍微简化了问题,省略了 cmets 的逻辑(无论如何,这对我来说看起来很糟糕)。

#include <map>
#include <fstream>
#include <iostream>
#include <string>

typedef std::pair<std::string, std::string> entry;

// This isn't officially allowed (it's an overload, not a specialization) but is
// fine with every compiler of which I'm aware.
namespace std {
std::istream &operator>>(std::istream &is,  entry &d) { 
    std::getline(is, d.first, '=');
    std::getline(is, d.second);
    return is;
}
}

int main() {
    // open an input file.
    std::ifstream in("myfile.ini");

    // read the file into our map:
    std::map<std::string, std::string> dict((std::istream_iterator<entry>(in)),
                                            std::istream_iterator<entry>());

    // Show what we read:
    for (entry const &e : dict) 
        std::cout << "Key: " << e.first << "\tvalue: " << e.second << "\n";
}

就个人而言,我认为我会将注释跳过写为过滤流缓冲区,但对于那些不熟悉 C++ 标准库的人来说,这可能是一个有点迂回的解决方案。另一种可能性是 comment_iterator 从指定的注释分隔符开始跳过一行的其余部分。我也不喜欢这样,但在某些方面它可能更简单。

请注意,我们在此处真正编写的唯一代码是将文件中的单个条目读取到pair 中。 istream_iterator 从那里处理几乎所有事情。因此,编写函数的直接模拟几乎没有什么意义——我们只需从迭代器中初始化映射,就完成了。

【讨论】:

    【解决方案2】:

    直接回答您的问题:当然可以迭代属性树。其实这很简单:

    #include <boost/property_tree/ptree.hpp>
    #include <boost/property_tree/ini_parser.hpp>
    
    int main()
    {
        using boost::property_tree::ptree;
        ptree pt;
    
        read_ini("input.txt", pt);
    
        for (auto& section : pt)
        {
            std::cout << '[' << section.first << "]\n";
            for (auto& key : section.second)
                std::cout << key.first << "=" << key.second.get_value<std::string>() << "\n";
        }
    }
    

    这会产生如下输出:

    [Cat1]
    name1=100 #skipped
    name2=200 \#not \\skipped
    name3=dhfj dhjgfd
    [Cat_2]
    UsagePage=9
    Usage=19
    Offset=0x1204
    [Cat_3]
    UsagePage=12
    Usage=39
    Offset=0x12304
    

    我之前用写过一个功能非常齐全的Inifile解析器:

    它支持 cmets(单行和块)、引号、转义等。

    (作为奖励,它可以选择记录所有已解析元素的确切源位置,这是该问题的主题)。

    不过,出于您的目的,我想我会推荐 Boost Property Tree。

    【讨论】:

    • 谢谢,我可以迭代了。但是由于某种原因,值没有被打印出来。 IE。我只看到 [Cat1] [Cat_2] [Cat_3] 作为输出,没有“name1”“name2”等。我稍后会尝试解决这个问题,但如果你知道解决方案,我会很感激你的帮助 :)
    • “不过,出于你的目的,我想我会推荐 Boost Property Tree。” - 我不明白你到底推荐什么。您是否建议不要使用ini_parser?你推荐使用xml 而不是ini 还是什么?
    • @javapowered 你知道,我停在那里是因为你要求这样做。当然,您也可以只打印这些值。我现在已经添加了
    • @javapowered 请不要tell people to use std::endl 而不是\n,除非您知道自己在做什么。在这种情况下,它是[s]ane。
    • 我总是使用 std::endl 而不是 \n 来遵循 c++ 风格。我在这里读到stackoverflow.com/questions/213907/c-stdendl-vs-n,\n 必须更快,但使用“
    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2011-12-30
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多