4

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

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# 代码模拟的最简单方法是什么?

4

2 回答 2

19

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

#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 解析器:

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

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

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

于 2013-04-21T19:58:09.837 回答
0

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

#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那里处理几乎所有东西。因此,编写函数的直接模拟几乎没有什么实际意义——我们只需从迭代器初始化映射,就完成了。

于 2013-04-21T19:57:41.947 回答