用另一个字符串替换字符串的一部分


186

在C ++中是否可以用另一个字符串替换一个字符串的一部分?

基本上,我想这样做:

QString string("hello $name");
string.replace("$name", "Somename");

但是我想使用标准C ++库。


1
在C中替换字符串的函数什么的可能重复项-抱歉,这不是C ++,而是C语言;我希望我能投票。
polygenelubricants 2010年

1
@poly我想这也一定是C ++的要求,但我找不到它
Michael Mrozek 2010年

1
这个问题上有一个std标签,但也许您可能对boost的字符串算法感兴趣,它还包括多种替换算法(就位/复制,区分大小写/不区分大小写,替换第一个/最后一个/全部/第n个) )。
UncleBens

@Michael Mrozek stackoverflow.com/questions/3418231 / ...上已经结束了,但是它是新的,这里的replaceAll方法更加健壮。
dave-holm

Answers:


288

有一个函数可以在字符串(find)中找到子字符串,并且可以用另一个字符串(replace)替换字符串中的特定范围,因此您可以将它们组合以获得所需的效果:

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

为了回应评论,我认为replaceAll可能看起来像这样:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

2
如果原始字符串包含一个以上的“ $ name”实例,而我想替换所有实例,我将如何解决。
汤姆·里斯

1
为什么不fromto每通过const参考?如果from不存在,您的功能是什么?-1为此。
2010年

10
@sbi固定,虽然您可以将其表述为建议而不是攻击,但是它对我而言根本没有发生,我很少考虑使用const,如果我编写了这样的实用程序方法,则只有在知道了替换有效
Michael Mrozek 2010年

10
@Michael:好,我把我的不赞成票变成了赞成票。解雇const无视C ++的最佳工具之一。const引用传递应该是功能参数的默认模式。(如果没有FTR,则const您甚至无法将字符串文字传递给函数,因为您无法将临时变量绑定到非const引用。因此该函数甚至都不会执行写入的操作。)
sbi 2010年

19
这仍然是2018年唯一的解决方案吗?如果是这样,并且有任何C ++委员会正在阅读,请进行整理。太丢脸了。请分割(字符串,字符串)和替换(字符串,字符串)!
user997112 '18

96

在C ++ 11中,您可以这样使用std::regex

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

要转义转义字符,必须使用双反斜杠。


我很确定std::regex_replace不接受Qt的字符串。
BartoszKP 2015年

1
你是对的。发生这种情况时,QString提供了一个替换方法,该方法接受QRexExp,允许使用Qt自己的东西。但我认为,目前的答案可以通过更换得到纠正stringstring.toStdString()
汤姆(Tom)

1
或仅更改Stringstd::string,因为问题与Qt不相关。请考虑这样做-之后,我们会很乐意为您答复。
BartoszKP 2015年

5
原始字符串允许写R"(\$name)"而不是"\\$name"
Jarod42 '16

2
如果不考虑std :: regex的构造时间,这比查找/替换要慢多少?
jw_

18

std::string有一种replace方法,就是您要寻找的吗?

您可以尝试:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

我还没有尝试过自己,只是阅读的文档find()replace()


2
从我可以看到,std :: string replace方法不会像我想要的那样接受两个字符串。
汤姆·里斯

2
这对我不起作用。sizeof应该替换为string(“ Somename”)。size()-1
TimZaman

@TimZaman:令我感到困惑的是,文档明确指出您可以从C样式的字符串进行初始化。
SC Madsen 2014年

5
第二个参数应该是“ $ name”的长度(而不是“ Somename”的长度),不是吗?
Daniel Kiss

10

要返回新字符串,请使用以下命令:

std::string ReplaceString(std::string subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

如果需要性能,下面是一个优化的函数,它可以修改输入字符串,但不会创建该字符串的副本:

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

测试:

std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;

std::cout << "ReplaceString() return value: " 
          << ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not modified: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

输出:

Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def

您在ReplaceStringInPlace()中对subject.replace的调用是否真的是在原地修改字符串?
达米安

我简要地看了一下源代码,看起来它确实使用了移动语义将旧字符串的前端移到适当位置,因此不会被复制,但是插入的新片段将被复制到旧字符串和旧字符串的尾部中被复制到旧字符串的调整大小后的缓冲区中。字符串可能会扩展得太多,从而重新分配了整个基础缓冲区,但是如果按照他的示例将1替换为1,我认为它确实是“就地”发生的,或者没有任何复制,但是如果扩展字符串,只有旧字符串的第一部分才被复制,只有那时才被复制。
微粒

6

是的,您可以执行此操作,但是必须使用string的find()成员找到第一个字符串的位置,然后使用replace()成员进行替换。

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

如果您打算使用标准库,则应该真正拿到本书《 C ++标准库》的副本,该书 很好地涵盖了所有内容。


1
它的的size_t而不是SIZE_TYPE
雷沃

1
它是std :: string :: size_type,而不是size_t或未经修饰的size_type。
jmucchiello

5

我通常这样使用:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

它反复调用std::string::find()以查找搜索到的字符串的其他出现,直到std::string::find()找不到任何东西为止。因为std::string::find()返回匹配的位置,所以我们没有使迭代器无效的问题。


4

这听起来像一个选择

string.replace(string.find("%s"), string("%s").size(), "Something");

您可以将其包装在一个函数中,但是这种单行解决方案听起来可以接受。问题在于,这只会更改第一次出现的内容,您可能希望对其进行循环,但是它还允许您使用相同的标记(%s)将多个变量插入此字符串中


1
像风格一样,但发现不同的字符串令人困惑^^str.replace(str.find("%s"), string("%s").size(), "Something");
PaulWürtz'17

3

如果所有字符串都是std :: string,则使用时会发现字符截断的奇怪问题,sizeof()因为它用于C字符串,而不是C ++字符串。解决方法是使用的.size()类方法std::string

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

这取代了sHaystack内联-无需在其上进行=分配。

用法示例:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;

2
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);

2

如果要快速执行此操作,可以使用两次扫描方法。伪代码:

  1. 首先解析。查找多少个匹配字符。
  2. 扩展字符串的长度。
  3. 第二次解析。当我们得到要替换的匹配项时,从字符串的末尾开始,否则我们仅从第一个字符串中复制字符。

我不确定是否可以将其优化为就地算法。

还有一个C ++ 11代码示例,但我只搜索一个字符。

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}

1
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}

2
能否请您解释一下此代码(在您的答案中)?这样您可能会获得更多投票!
戴帽子的家伙

1

我自己的实现考虑到字符串只需要调整一次大小,然后替换就可以发生了。

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

用法可能是这样的:

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");

0

我现在正在学习C ++,但是编辑以前发布的一些代码,我可能会使用类似的东西。这使您可以灵活地替换1个或多个实例,还可以指定起点。

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}

0

这可能会更好用

void replace(string& input, const string& from, const string& to)
{
    while(true)
    {
        size_t startPosition = input.find(from);
        if(startPosition == string::npos)
            break;
        input.replace(startPosition, from.length(), to);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.