用单引号(')替换双单引号('')。

5
假设我有一个字符串:
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吗?

1
@hydra123,我相信你要找的是replace_all()函数。 - PeskyPotato
@sehe 这个问题是不同的。最好把整件事都看完! - hydra123
@hydra123 相信我,我知道。在这方面,我客观地比任何人都付出了更多的努力。[https://dev59.com/TKPia4cB1Zd3GeqP2bGb#45281989] - sehe
祝你好运。我不会回去找出到底是哪些人“支持正则表达式”。我只注意到你似乎无法使用正则表达式使代码工作。与其事后抱怨,你可以简单地说出为什么拒绝答案。然而,你根本没有对答案做出任何回应(这是你第一次说出任何相关的话,比如“我有不想要的原因” - 尽管不清楚你不想要什么:毕竟你还是在写解析器,但却用了名为正则表达式的拐杖)。 - sehe
我并不介意回答很多问题。但在这种情况下,你提出的问题序列表明你不知道自己在做什么。我关心这个问题:我想向你展示parsing into datastructures,解释character escapes是什么以及escapes are a presentation-only thing。我只是希望人们学会正确的方法,并知道为什么。这就是我们在这里的原因。 - sehe
显示剩余6条评论
2个回答

7

好的。你连续6个问题都在问关于解析工作的事情。

许多人告诉过你,正则表达式不是这项工作的适当工具。 包括我

enter image description here

我向您展示了一个Spirit X3语法的示例,可以将此配置字符串解析为键值映射,并正确解释转义引号(例如'\\'')(请参见此处

  • 我在此基础上进行了扩展,使重复引号可以用来转义引号(请参见此处),仅增加了13个字符。

我的所有示例都有一个优点,那就是它们已经解析了键和值,因此您拥有一个正确的配置设置映射。

然而,在您最新的问题中(提取除正则表达式指定部分以外的所有内容),您仍然要求解决方案。

当然,答案已经在我的第一个回答中。

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

发布了这篇文章,并附上了一个C++03版本,可以在Coliru上实时查看

编写手动解析器

如果您因为不理解而拒绝使用它,只需询问即可。

如果您“不想”使用Spirit,您可以轻松地手动编写类似的解析器。我没有这样做,因为它很繁琐且容易出错。以下是供您参考:

  1. 仍然是c++03
  2. 仅使用标准库功能
  3. 仍然解析具有可转义引号的单/双引号字符串
  4. 仍然解析为map<string,string>
  5. 在无效输入时提供信息性错误消息

重点:像人们从第一天起一直敦促您那样使用适当的语法

在 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. 避免在cpp中出现空令牌
  2. 使用正则表达式提取cpp中的空格
  3. 使用boost token迭代器提取单引号和括号之间的值的正则表达式
  4. 在CPP中分词字符串,接受给定字符集之间的所有内容
  5. 提取圆括号和单引号之间带单引号的字符串
  6. 提取除了正则表达式指定的内容之外的所有内容
  7. 这个

-1

使用 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);

@matteo-italia 我想我误解了问题,现在我附上了一个更正的解决方案。 - PeskyPotato
也许是因为它以不回答楼主问题的方式开始? - juanchopanza
1
  1. 函数返回类型应为 std::string
  2. s_to_replace.search 存在语法错误。
  3. 可以使用单个调用 std::string::replace 来执行 erase/insert 操作。
  4. 需要检查 s_to_replace 是否为空,以避免无限循环。
  5. 请参见 此处 以获取类似的实现。
- Blastfurnace
1
请注意,erase/insert 可以工作,但我认为它存在性能问题。erase 会留下一个空洞,所有后面的字符都会向左移动以填充它。然后 insert 将所有相同的字符向右移动以打开新字符串的空洞。一个单独的 std::string::replace 应该可以将这个工作减少一半。 - Blastfurnace
我应该更清晰地表达,你需要检查s_to_replace是否为空。std :: string :: find总是在<= s.size()的位置匹配空字符串。这会导致此代码中的错误。 - Blastfurnace
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接