【问题标题】:Replace double single quote (' ') with a single quote (')将双单引号 (' ') 替换为单引号 (')
【发布时间】:2017-12-29 13:53:21
【问题描述】:

假设我有一个字符串:

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

现在我正在使用以下代码来提取令牌:

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

代码:

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.

现在将其放入字符串向量后,如何转换标记/字符串以将双引号替换为单引号:

例如:

'm1.labs.terad ''ata.com' 应该变成 'm1.labs.terad 'ata.com''us ''er5' 应该变成 'us 'er5'

我可以为此使用 boost::replace_all 吗?

【问题讨论】:

  • 你为什么不写一个函数,遍历每个标记,当找到两个连续的'时,只取其中一个。
  • 不清楚您在问什么,在第一个示例中您删除了两个引号,在第二个示例中您将它们替换为一个。
  • @MatteoItalia 我的错。抱歉已更正。
  • 嗨,这样好吗:Boost::replace_all(s," ' ' "," ' "); ??
  • @hydra123 replace_all() 会起作用,我相信这就是你要找的。​​span>

标签: c++ string boost replace


【解决方案1】:

好的。您一直在询问有关此解析作业的 6 个问题¹。

许多人一直在告诉您正则表达式不是这项工作的工具。 Including me:

我已经给你看了

  • Spirit X3 语法示例,可将此配置字符串解析为键值映射,正确解释转义引号(例如'\\'')(参见here
  • 我对其进行了扩展(13 个字符)以允许重复引用以转义引用(请参阅here

我的所有示例都非常出色,因为它们已经解析了键和值,因此您拥有正确的配置设置映射。

但你仍然在最新的问题 (Extract everything apart from what is specified in the regex) 中要求它。

当然答案就在我的第一个答案中:

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

posted this 以及它的 C++03 版本 live on Coliru

编写手动解析器

如果你因为不理解而拒绝它,你所要做的就是问。

如果您“不想”使用 Spirit,您可以轻松地手动编写类似的解析器。我没有,因为它很乏味且容易出错。如果你需要它来获得灵感,你可以在这里:

  1. 还是c++03
  2. 仅使用标准库功能
  3. 仍在使用可转义引号解析单引号/双引号字符串
  4. 仍解析为map&lt;string, string&gt;
  5. 在无效输入时引发信息性错误消息

底线:使用正确的语法,就像人们从第一天开始就敦促你的那样

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

一如既往地打印:

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

¹见

  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. 这个

【讨论】:

    【解决方案2】:

    使用 For 循环将字符串中的子字符串替换为子字符串

    这里我们用另一个子字符串替换一个子字符串,并返回修改后的字符串。我们传入要更改的字符串、要查找的字符串以及要替换的字符串,ss_to_replaces_replace

    find() 搜索并找到传入字符串的第一个字符,并在该位置返回一个迭代器。 std::string::npos 这个值是size_t 可以达到的最大可能值,即字符串的结尾。 std::string::erase 获取第一个字符的位置和要替换的字符数并删除它们。 std::string::insert 获取要插入的位置和要插入的字符串的位置,并执行此操作。

    std::string replace_substring(string s, const string s_to_replace, const string s_replace) {
        for(size_t position = 0; ; position += s_replace.length()) {
    
            position = s.find(s_to_replace, position);
    
            if(position == string::npos || s.empty()) break;
    
            s.erase(position, s_to_replace.length());
            s.insert(position, s_replace);
            // s.replace(position, s_to_replace.length(), s_replace)
        }
        return s;
    }
    

    使用 Boost 将子字符串替换为字符串中的子字符串

    #include <boost/algorithm/string/replace.hpp>
    
    boost::replace_all(s, s_to_replace, s_replace);
    

    【讨论】:

    • 这行不通,你正在用另一个单个字符替换单个字符,而 OP 实际上想要删除一个字符。
    • @matteo-italia 我想我误解了这个问题,我现在添加了一个更正的解决方案。
    • 我不明白,为什么投反对票?这不回答 OP 的问题吗?
    • 1) 函数返回类型应为std::string。 2) s_to_replace.search 有语法错误。 3) erase/insert 可以通过调用std::string::replace 来完成。 4)您需要检查s_to_replace是否为空以避免无限循环。 5) 类似的实现参见here
    • 请注意,erase/insert 会起作用,但我认为它存在性能错误。 erase 打了一个洞,后面的所有字符都向左移动以填充它。然后insert 将所有相同的字符向右移动,为新字符串打开一个洞。单个std::string::replace 应该可以将这项工作减半。
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2015-08-01
    • 2020-12-28
    • 1970-01-01
    相关资源
    最近更新 更多