Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace double single quote (' ') with a single quote (')

Suppose I have a string :

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

Now I am using the following code to extract the tokens:

'm1.labs.terad  ''ata.com'  <- token1
'us ''er5'                    <-token2
'user)5'                    <-token3
'def\ault'                  <-token4

Code:

regex re("(\'(.*?)\'\)");
typedef std::vector<std::string> StringVector;
StringVector arg_values;
boost::regex re_arg_values("('[^']*(?:''[^']*)*')");
boost::sregex_token_iterator name_iter_start(argsStr.begin(),argsStr.end(), re_arg_values, 0),name_iter_end;
std::copy(value_iter_start, value_iter_end,std::back_inserter(arg_values)); 
//putting the token in the string vector.

Now after putting it into the string vector, How can I convert the tokens/ string to replace double quotes with single quotes:

For example:

'm1.labs.terad ''ata.com' should become 'm1.labs.terad 'ata.com' and 'us ''er5' should become 'us 'er5'.

Can I use boost::replace_all for this?

like image 769
hydra123 Avatar asked Jul 24 '17 04:07

hydra123


1 Answers

Okay. You've been asking about this parsing jobs for 6 questions straight¹.

Many people have been telling you regex is not the tool for the job. Including me:

enter image description here

I've shown you

  • An example of a Spirit X3 grammar that parses this config string into a key-value map, correctly intepreting escaped quotes ('\\'' e.g.) (see here)
  • I expanded on it (in 13 characters) to allow for repeated quotes to escape a quote (see here)

All my examples have been superior in that they already parse the keys along with the values, so you have a proper map of config settings.

Yet you still ask for it in you latest question (Extract everything apart from what is specified in the regex).

Of course the answer was in my very first answer:

for (auto& setting : parse_config(text))
    std::cout << setting.first << "\n";

I posted this along with a C++03 version of it live on Coliru

Writing The Manual Parser

If you are rejecting it because you don't understand, all you had to do is ask.

If you "don't wanna" use Spirit, you can easily write a similar parser manually. I didn't, because it is tedious and error prone. Here you are in case you need it for inspiration:

  1. still c++03
  2. using only standard library features
  3. still parsing single/double-quoted strings with escapable quotes
  4. still parses into map<string, string>
  5. raises informative error messages on invalid input

BOTTOM LINE: Use a proper grammar like people have been urging you since day 1

Live On Coliru

#include <iostream>
#include <sstream>
#include <map>

typedef std::map<std::string, std::string> Config;
typedef std::pair<std::string, std::string> Entry;

struct Parser {
    Parser(std::string const& input) : input(input) {}
    Config parse() {
        Config parsed;

        enum { KEY, VALUE } state = KEY;
        key = value = "";
        f = input.begin(), l = input.end();

        while (f!=l) {
            //std::cout << "state=" << state << ", '" << std::string(It(input.begin()), f) << "[" << *f << "]" << std::string(f+1, l) << "'\n";
            switch (state) {
              case KEY:
                  skipws();
                  if (!parse_key())
                      raise("Empty key");

                  state = VALUE;
                  break;
              case VALUE:
                  if (!expect('(', true))
                      raise("Expected '('");

                  if (parse_value('\'') || parse_value('"')) {
                      parsed[key] = value;
                      key = value = "";
                  } else {
                      raise("Expected quoted value");
                  }

                  if (!expect(')', true))
                      raise("Expected ')'");

                  state = KEY;
                  break;
            };
        }

        if (!(key.empty() && value.empty() && state==KEY))
            raise("Unexpected end of input");

        return parsed;
    }

  private:
    std::string input;

    typedef std::string::const_iterator It;
    It f, l;
    std::string key, value;

    bool parse_key() {
        while (f!=l && alpha(*f))
            key += *f++;
        return !key.empty();
    }

    bool parse_value(char quote) {
        if (!expect(quote, true))
            return false;

        while (f!=l) {
            char const ch = *f++;
            if (ch == quote) {
                if (expect(quote, false)) {
                    value += quote;
                } else {
                    //std::cout << " Entry " << key << " -> " << value << "\n";
                    return true;
                }
            } else {
                value += ch;
            }
        }

        return false;
    }

    static bool space(unsigned char ch) { return std::isspace(ch); }
    static bool alpha(unsigned char ch) { return std::isalpha(ch); }
    void skipws() { while (f!=l && space(*f)) ++f; }
    bool expect(unsigned char ch, bool ws = true) {
        if (ws) skipws();
        if (f!=l && *f == ch) {
            ++f;
            if (ws) skipws();
            return true;
        }
        return false;
    }

    void raise(std::string const& msg) {
        std::ostringstream oss;
        oss << msg << " (at '" << std::string(f,l) << "')";
        throw std::runtime_error(oss.str());
    }
};

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

    Config cfg = Parser(text).parse();

    for (Config::const_iterator setting = cfg.begin(); setting != cfg.end(); ++setting) {
        std::cout << "Key " << setting->first << " has value " << setting->second << "\n";
    }

    for (Config::const_iterator setting = cfg.begin(); setting != cfg.end(); ++setting) {
        std::cout << setting->first << "\n";
    }
}

Prints, as always:

Key dbname has value def\ault
Key password has value user)5
Key server has value m1.labs.terad  'ata.com
Key username has value us\* er5
dbname
password
server
username

¹ see

  1. avoid empty token in cpp
  2. extracting whitespaces using regex in cpp
  3. Regex to extract value between a single quote and parenthesis using boost token iterator
  4. tokenizing string , accepting everything between given set of characters in CPP
  5. extract a string with single quotes between parenthesis and single quote
  6. Extract everything apart from what is specified in the regex
  7. this one
like image 185
sehe Avatar answered Nov 15 '22 03:11

sehe