2013-04-21 2 views
3

Моя задача тривиальна - я просто нужно разобрать этот файл:перебираем файл ini на C++, возможно, используя boost :: property_tree :: ptree?

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++?

+1

_Возможно, можно перебирать ptree, на самом деле это тривиально, так как мой ответ теперь показывает (обновляется) (примечание: program-options! = Property-tree) – sehe

ответ

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 

Я написал очень полнофункциональный INIFILE анализатор с использованием перед:

Он поддерживает комментарии (одиночная строка и блок), котировки, экраны и т. Д.

(в качестве бонуса он может записывать точное местоположение источника всех проанализированных элементов, что было предметом этого вопроса).

Для вашей цели, тем не менее, я думаю, что я бы пересмотрел Boost Property Tree.

+0

спасибо, я могу итератировать.Однако по какой-то причине значения не печатаются. То есть Я вижу только [Cat1] [Cat_2] [Cat_3] в качестве вывода, и нет «name1» «name2» и т. д. Я попытаюсь решить эту проблему немного позже, но если вы знаете решение, я был бы признателен за вашу помощь :) – javapowered

+0

«Для вашей цели, однако, я думаю, что я бы recomment Boost Property Tree». - Я не понимаю, что именно вы рекомендуете. вы рекомендуете не использовать 'ini_parser'? вы рекомендуете использовать 'xml' вместо' ini' или что? – javapowered

+0

@javapowered, вы знаете, я остановился там, потому что вы просили об этом. Вы могли бы, конечно, просто распечатать значения. Я добавил его сейчас – sehe

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 обрабатывает почти все, что оттуда. Таким образом, в написании прямого аналога вашей функции мало реальности - мы просто инициализируем карту из итераторов, и все готово.