68

I have a ini file which contains some sample values like:

[Section1]
Value1 = 10
Value2 = a_text_string

I'm trying to load these values and print them in my application with Boost but I don't understand how to do this in C++.

I searched in this forum in order to find some examples (I always used C and so I'm not very good in C++) but I found only examples about how to read values from file all at once.

I need to load just a single value when I want, like string = Section1.Value2 because I don't need to read all the values, but just few of them.

I'd like to load single values and to store them in variable in order to use them when I want in my application.

It is possible to do this with Boost?

At the moment, I'm using this code:

#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;
   }

But this just read all the values in a for loop; at the contrary I just want to read single values when I want and I don't need to insert values in the file, because it is already written with all the values which I need in my program.

Null
  • 1,950
  • 9
  • 30
  • 33
Marcus Barnet
  • 2,083
  • 6
  • 28
  • 36

4 Answers4

152

You can also use Boost.PropertyTree to read .ini files:

#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;
Timo
  • 5,125
  • 3
  • 23
  • 29
  • 1
    what if I do not know certain set of available keys, but I still want to get full list of key=value pairs? I.e. how would you rewrite your program without using "Section1.Value1" and "Section1.Value2" constants? – Oleg Vazhnev Apr 21 '13 at 19:02
  • upd: i've found this question http://stackoverflow.com/questions/11065938/boost-program-options-get-all-entries-in-section according to it is is not possible without writing custom code... – Oleg Vazhnev Apr 21 '13 at 19:08
  • 2
    @javapowered The question you've found is about Boost.Program_options, not about Boost.PropertyTree – user Feb 25 '14 at 16:02
  • 2
    And here it is solved: http://stackoverflow.com/questions/16135285/iterate-over-ini-file-on-c-probably-using-boostproperty-treeptree – user Feb 25 '14 at 16:10
  • The problem I have always had with Boost.PropertyTree is the use of , between section names and value names. True INI files support section names and value names that include the '.' character. A far better separator would be / or \. A non printable character such as the bell character (\007) would be even better. Is it possible to customize this behavior? – Benilda Key Aug 01 '15 at 11:42
  • This is good, but the lib-boost-dev installation is huge in ububut > 50 Mb; I tried giving the boost/property_tree/ folder; but headers are referring to other headers outside; Finally used simileini.h https://github.com/brofield/simpleini – Alex Punnen May 03 '17 at 12:30
  • Instead of "ptree" I recommend using case insensitive "iptree". Windows ini files are normally case insensitive. – Muxecoid Jun 24 '20 at 14:39
3

Parsing INI files is easy due to their simple structure. Using AXE I can write in a few lines to parse sections, properties and comments:

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();

More detailed example can be found in the Reference.pdf

Regarding not reading the whole file, it can be done in different ways. First of all, parser for INI format requires at least forward iterators, so you can't use stream iterators, since they are input iterators. You can either create a separate class for stream with required iterators (I wrote one such class in the past with sliding buffer). You can use memory mapped file. Or you can use a dynamic buffer, reading from the standard stream and supplying to parser until you found the values. If you don't want to have a real parser, and don't care if the INI file structure is correct or not, you can simply search for your tokens in the file. Input iterators would suffice for that.

Finally, I'm not sure that avoiding reading the whole file brings any advantages with it. INI files are typically pretty small, and since the hard drive and multiple buffering systems would read one or more sectors anyway (even if you need just one byte), so I doubt there would be any performance improvement by trying to read file partially (especially doing it repeatedly), probably the opposite.

Gene Bushuyev
  • 5,512
  • 20
  • 19
2

The file needs to be parsed, which has to be done sequentially. So I'd just read the whole file, store all the values in some collection (map or unordered_map, probably, either using pair<section, key> as key or using map of maps) and fetch them from there when needed.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
2

I have read a nice article about INI-parsing with boost methods, it's called INI file reader using the spirit library by Silviu Simen.

It's simple one.

mosg
  • 12,041
  • 12
  • 65
  • 87