Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
274 views
in Technique[技术] by (71.8m points)

c++ - How to expand environment variables in .ini files using Boost

I have a INI file like

[Section1]
Value1 = /home/%USER%/Desktop
Value2 = /home/%USER%/%SOME_ENV%/Test

and want to parse it using Boost. I tried using Boost property_tree like

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;

But it didn't expand the environment variable. Output looks like

/home/%USER%/Desktop
/home/%USER%/%SOME_ENV%/Test

I was expecting something like

/home/Maverick/Desktop
/home/Maverick/Doc/Test

I am not sure if it is even possible with boost property_tree.

I would appreciate any hint to parse this kind of file using boost.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

And here's another take on it, using the old crafts:

  • not requiring Spirit, or indeed Boost
  • not hardwiring the interface to std::string (instead allowing any combination of input iterators and output iterator)
  • handling %% "properly" as a single % 1

The essence:

#include <string>
#include <algorithm>

static std::string safe_getenv(std::string const& macro) {
    auto var = getenv(macro.c_str());
    return var? var : macro;
}

template <typename It, typename Out>
Out expand_env(It f, It l, Out o)
{
    bool in_var = false;
    std::string accum;
    while (f!=l)
    {
        switch(auto ch = *f++)
        {
            case '%':
                if (in_var || (*f!='%'))
                {
                    in_var = !in_var;
                    if (in_var) 
                        accum.clear();
                    else
                    {
                        accum = safe_getenv(accum);
                        o = std::copy(begin(accum), end(accum), o);
                    }
                    break;
                } else 
                    ++f; // %% -> %
            default:
                if (in_var)
                    accum += ch;
                else
                    *o++ = ch;
        }
    }
    return o;
}

#include <iterator>

std::string expand_env(std::string const& input)
{
    std::string result;
    expand_env(begin(input), end(input), std::back_inserter(result));
    return result;
}

#include <iostream>
#include <sstream>
#include <list>

int main()
{
    // same use case as first answer, show `%%` escape
    std::cout << "'" << expand_env("Greeti%%ng is %HOME% world!")  << "'
";

    // can be done streaming, to any container
    std::istringstream iss("Greeti%%ng is %HOME% world!");
    std::list<char> some_target;

    std::istreambuf_iterator<char> f(iss), l;
    expand_env(f, l, std::back_inserter(some_target));
    std::cout << "Streaming results: '" << std::string(begin(some_target), end(some_target)) << "'
";

    // some more edge cases uses to validate the algorithm (note `%%` doesn't
    // act as escape if the first ends a 'pending' variable)
    std::cout << "'" << expand_env("")                           << "'
";
    std::cout << "'" << expand_env("%HOME%")                     << "'
";
    std::cout << "'" << expand_env(" %HOME%")                    << "'
";
    std::cout << "'" << expand_env("%HOME% ")                    << "'
";
    std::cout << "'" << expand_env("%HOME%%HOME%")               << "'
";
    std::cout << "'" << expand_env(" %HOME%%HOME% ")             << "'
";
    std::cout << "'" << expand_env(" %HOME% %HOME% ")            << "'
";
}

Which, on my box, prints:

'Greeti%ng is /home/sehe world!'
Streaming results: 'Greeti%ng is /home/sehe world!'
''
'/home/sehe'
' /home/sehe'
'/home/sehe '
'/home/sehe/home/sehe'
' /home/sehe/home/sehe '
' /home/sehe /home/sehe '

1 Of course, "properly" is subjective. At the very least, I think this

  • would be useful (how else would you configure a value legitimitely containing %?)
  • is how cmd.exe does it on Windows

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...