0

I have the following code :

#include <iostream>
#include <regex>

using namespace std;

int main()
{
    string s;

    s = "server ('m1.labs.terad  ''ata.com') username ('us\* er5') password('user)5') dbname ('def\\ault')";

    regex re("('[^']*(?:''[^']*)*')");
    // I have used -1 to extract everything apart from the content there in brackets.
    sregex_token_iterator i(s.begin(), s.end(), re, -1);
    sregex_token_iterator j;

    unsigned count = 0;
    while(i != j)
    {
        cout <<*i<< endl;
        count++;
        i++;
    }
    cout << "There were " << count << " tokens found." << endl;

    return 0;
}

The regex above is meant to extract the argument name such as server, username, password etc.

But this is the output I am getting:

server (
) username (
) password(
) dbname (
)
There were 5 tokens found.

But the output I was expecting is :

server
username
password
dbname
There were 4 tokens found.

Please help me where I am missing out. Thanks in advance

hnefatl
  • 5,860
  • 2
  • 27
  • 49
hydra123
  • 337
  • 5
  • 14
  • Again, you have a redundant backslash in your input string literal. Please provide a *literal string* you are dealing with. And beside the examples, please formulate *the verbal rules* for extraction. Once you do it, you won't have to ask almost identical questions. – Wiktor Stribiżew Jul 24 '17 at 07:35
  • Change your regexp so it matches what you want to extract, not what you want to discard. You can use capture groups or lookarounds. – Barmar Jul 24 '17 at 07:40
  • 2
    Short question: Why is this tagged [tag:boost] if you're using `std::regex` and not `boost::regex`? Or am I missing something? – muXXmit2X Jul 24 '17 at 07:44
  • @muXXmit2X In my code sregex_token_iterator falls under boost . thats why I used. – hydra123 Jul 24 '17 at 08:08
  • @WiktorStribiżew I formulated very well, what exactly is expected and I don't think there is a redundant backslash. That is how my input looks like – hydra123 Jul 24 '17 at 08:09
  • @hydra123 No, `"\*"` (string literal) = `"*"` (sl) = `*` (literal string). – Wiktor Stribiżew Jul 24 '17 at 08:11
  • @Barmar I cannot come up with a regex like that, hence trying the above code – hydra123 Jul 24 '17 at 08:27
  • @WiktorStribiżew My string I want to escape single quote by using double quote and remaining things are extracted using a backslash(including a backslash) – hydra123 Jul 24 '17 at 08:31

1 Answers1

0

Out of spite pity, here's the solution based on the previous answer:
extract a string with single quotes between parenthesis and single quote

Change main to:

int main() {
    auto const text = "server ('m1.labs.terad  ''ata.com') username ('us\\* er5') password('user)5') dbname ('def\\ault')";

    Config cfg = parse_config(text);

    for (auto& setting : cfg)
        //std::cout << "Key " << setting.first << " has value " << setting.second << "\n";
        std::cout << setting.first << "\n";
}

Prints

dbname
password
server
username

Live On Coliru

#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <map>

using Config = std::map<std::string, std::string>;
using Entry  = std::pair<std::string, std::string>;

namespace parser {
    using namespace boost::spirit::x3;

    namespace {
        template <typename T> struct as_type {
            template <typename Expr>
            auto operator[](Expr expr) const { return rule<struct _, T>{"as"} = expr; }
        };

        template <typename T> static const as_type<T> as = {};
    }
    auto quoted = [](char q) { return lexeme[q >> *(q >> char_(q) | '\\' >> char_ | char_ - q) >> q]; };

    auto value  = quoted('\'') | quoted('"');
    auto key    = lexeme[+alpha];
    auto pair   = key >> '(' >> value >> ')';
    auto config = skip(space) [ *as<Entry>[pair] ];
}

Config parse_config(std::string const& cfg) {
    Config parsed;
    auto f = cfg.begin(), l = cfg.end();
    if (!parse(f, l, parser::config, parsed))
        throw std::invalid_argument("Parse failed at " + std::string(f,l));
    return parsed;
}

int main() {
    auto const text = "server ('m1.labs.terad  ''ata.com') username ('us\\* er5') password('user)5') dbname ('def\\ault')";

    Config cfg = parse_config(text);

    for (auto& setting : cfg)
        //std::cout << "Key " << setting.first << " has value " << setting.second << "\n";
        std::cout << setting.first << "\n";
}

Prints

dbname
password
server
username
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Note: I solved an issue that you might have run into with older (GCC) compilers. I took the more elaborate `as<>[]` directive from my [older answer](https://stackoverflow.com/questions/33816662/understanding-the-list-operator-in-boost-spirit/33817135#33817135) because I noticed some versions of GCC had problems compiling the lambda version. – sehe Jul 24 '17 at 12:07
  • In case your rejection of the Spirit approach is based on unspoken requirements, here's the exact same thing in C++03. Your compiler can handle that! http://coliru.stacked-crooked.com/a/f9f2fcc1bdd2a384 – sehe Jul 24 '17 at 12:19