scrap book

プログラミングとか

boostでiniファイルを読み込んでmapに詰め込む

いつも地味に面倒なiniからの読み込みを簡単にするためにboost::property_treeでiniファイルを読み込んでmap<string,string>に詰め込むサンプル。

読み込むiniファイル(sample.ini)
[sectionA]
key1        = val1

[section B]
spaced key  = spaced value

[section C]         # comment
key1        = 1     # duped name
key2        = あ    ; japanee

# comment
; comment
ソース(おためし)

まずは試しに表示するソースコード。普通にget_value()すると'='以降すべて取得できてしまうので、特定の文字以降をコメントとして扱いたい場合はsplit()してtrim()してあげる必要があるみたい。

#include <iostream>
#include <string>
#include <vector>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/algorithm/string.hpp>

void printIni2(const char* filepath)
{
    using boost::property_tree::ptree;
    ptree pt;

    read_ini( filepath, pt, std::locale("japanese") );

    std::vector<std::string> valStrList;
    for (auto& section : pt)
    {
        std::cout << '[' << section.first << "]" << std::endl;
        for (auto& key : section.second)
        {
            std::string valStr = key.second.get_value<std::string>();
            boost::split(valStrList, valStr, boost::is_any_of("#;"));

            std::cout << key.first << "=" << boost::algorithm::trim_copy(valStrList.at(0)) << std::endl;
        }
    }
}

void printIni(const char* filepath)
{
    using boost::property_tree::ptree;
    ptree pt;

    read_ini( filepath, pt, std::locale("japanese") );

    for (auto& section : pt)
    {
        // print part of "[section name]"
        std::cout << '[' << section.first << "]" << std::endl;

        // print part of "key = value"
        for (auto& key : section.second)
        {
            std::cout << key.first << "=" << key.second.get_value<std::string>() << std::endl;
        }
    }
}

int main(int argc, char* argv[])
{
    printIni("sample.ini");

    std::cout << "--------------------------------" << std::endl;
    printIni2("sample.ini");

    return 0;
}
結果
[sectionA]
key1=val1
[section B]
spaced key=spaced value
[section C]
key1=1     # duped name
key2=あ    ; japanee
--------------------------------
[sectionA]
key1=val1
[section B]
spaced key=spaced value
[section C]
key1=1
key2=あ
ソース(本題)

本題のソースコード

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

void ini2map(const char* filepath, std::map<std::string, std::string>& iniMap)
{
    using boost::property_tree::ptree;
    ptree pt;

    read_ini( filepath, pt, std::locale("japanese") );

    std::vector<std::string> valStrList;
    for (auto& section : pt)
    {
        std::string sectionName = section.first;

        for (auto& key : section.second)
        {
            std::string valStr = key.second.get_value<std::string>();
            boost::split(valStrList, valStr, boost::is_any_of("#;"));

            std::string keyName = key.first;
            std::string val = boost::algorithm::trim_copy(valStrList.at(0));

            std::cout << keyName << "=" << val << std::endl;

            iniMap[sectionName+"."+keyName] = val;
        }
    }
}

int main(int argc, char* argv[])
{
    std::map<std::string, std::string> iniMap;
    ini2map("sample.ini", iniMap);
    std::cout << iniMap.size() << std::endl;

    // print
    for( auto kvp : iniMap )
    {
        std::cout << kvp.first << "=" << kvp.second << std::endl;
    }
    // get value as int
    std::cout << boost::lexical_cast<int>(iniMap.at("section C.key1")) << std::endl;
    // get value (failure)
    try
    {
        std::string valStr = iniMap.at(" undefined key ");
    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}
結果
key1=val1
spaced key=spaced value
key1=1
key2=あ
4
section B.spaced key=spaced value
section C.key1=1
section C.key2=あ
sectionA.key1=val1
1
map::at
ソース(おまけ)

ぶっちゃけ以下のようなコードで取得できるだろうけど、プロパティツリーを意識しないようにしたかった。あとこの場合は行内コメントできない。

ptree pt;
read_ini("ini file", pt);
string strValue = pt.get<string>("[section].item");
int intValue = pt.get<int>("[section].item");


メモ:

  • forループの部分がこれで回るのはptreeが「map< string, map> >」みたいな入れ子構造になっているかららしい。
    • セクションがない項目は1階層目に入るので今回のコードだとうまく扱えない。
  • せっかくなのでread_iniのロケールにjapanese指定してみたけど、実は指定しなくてもSJISのファイル読めた。逆にjapanese指定してもutf-8のファイル読めた。この辺はよくわからない。
  • 環境:c++11 + boost 1.53


参考: