我的任务很简单 - 我只需要解析这样的文件:
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#代码模拟的最简单方法是什么?
答案 0 :(得分:18)
直接回答您的问题: 当然可以迭代属性树 。事实上,它是微不足道的:
#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
我之前使用boost-spirit编写了一个非常全功能的Inifile解析器:
它支持注释(单行和块),引号,转义等。
(作为奖励,它可选地记录所有已解析元素的确切源位置,这是该问题的主题)。
为了您的目的,我想我会推荐Boost Property Tree。
答案 1 :(得分: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
几乎可以处理所有内容。因此,编写函数的直接模拟几乎没有什么意义 - 我们只是从迭代器初始化地图,我们已经完成了。