如何替换字符串中所有出现的字符?

626

std::string 中,替换所有出现的字符为另一个字符有哪些有效的方法?


当涉及到这样的“高级”功能时,stdlib似乎很糟糕。最好在开始发现缺失的东西时使用QString或通用库。 - Kiruahxh
19个回答

951

std::string没有包含这样的函数,但您可以使用来自algorithm头文件的独立的replace函数。

#include <algorithm>
#include <string>

void some_func() {
  std::string s = "example string";
  std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}

10
std::string 是专门设计用于操作字符序列的一种容器。链接 - Kirill V. Lyadvinsky
231
很遗憾,这只能用来将一个字符替换为另一个字符。它无法将一个字符替换为更多的字符(也就是说,一个字符串)。有没有办法进行搜索和替换以实现多字符替换? - SasQ
7
如果我只想删除一个出现,该怎么办? - SIFE
6
当我使用这种方法将所有的x替换为y时,无论原始字符串是什么,结果都是一个冗长的y字符串。我很好奇,你认为问题出在哪里。(代码与你编写的完全相同) - Transcendent
10
иҝҷжӯЈжҳҜдҪҝз”Ёstd::string::replace()иҖҢдёҚжҳҜstd::replace()жүҖеҸ‘з”ҹзҡ„жғ…еҶөпјҒ 'x' (char)иў«йҡҗејҸиҪ¬жҚўдёә size_tеҖј[еҖјдёә120]пјҢеӣ жӯӨж•ҙдёӘеӯ—з¬ҰдёІжҲ–е…¶дёӯдёҖйғЁеҲҶе°Ҷиў«еЎ«е……дёә120дёӘ'y'зҡ„еүҜжң¬гҖӮ - IBue
显示剩余5条评论

169

这个问题的重点是字符替换,但我发现这个页面非常有用(特别是Konrad的评论),所以我想分享这个更通用的实现,它可以处理子字符串

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
    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(); // Handles case where 'to' is a substring of 'from'
    }
    return str;
}

使用方法:

std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;

输出:

Number_Of_Beans

XXjXugtXty

hhjhugthty


编辑:

如果您关心性能,可以以更合适的方式实现上述操作,通过直接修改传递的字符串参数str(通过引用而不是值传递)来进行“就地”更改。这样可以避免对原始字符串进行额外的昂贵复制。

代码:

static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
    // Same inner code...
    // No return statement
}
希望这对其他人有所帮助...

8
当源字符串较大且需要替换的字符串出现次数较多时,这个方法会出现性能问题。由于会多次调用string::replace()方法,因此会产生大量的字符串副本。请看我的解决方案,它可以解决这个问题。 - Ingmar
1
挑剔一点:按地址 => 按引用。它是不是一个地址只是实现细节。 - Max Truxa
1
你应该确保检查 from 字符串是否为空,否则会导致无限循环。 - newbie

151
我想我也可以加入boost solution
#include <boost/algorithm/string/replace.hpp>

// in place
std::string in_place = "blah#blah";
boost::replace_all(in_place, "#", "@");

// copy
const std::string input = "blah#blah";
std::string output = boost::replace_all_copy(input, "#", "@");

那么你的编译器缺少一些“-I”标志,以便它能够在系统上找到Boost库。也许你需要先安装它。 - Martin Ueding
1
以上代码更为高效,因为它使用的是标准库,而非全部使用boost库;-) - hfrmobile

48

假设有一个大的二进制 blob,其中所有的 0x00 字节都将被替换为 "\1\x30",所有的 0x01 字节都将被替换为 "\1\x31",因为传输协议不允许出现 \0 字节。

在以下情况下:

  • 替换和被替换的字符串具有不同的长度,
  • 源字符串中有许多要替换的字符串出现,且
  • 源字符串很大,

提供的解决方案无法应用(因为它们仅替换单个字符),或者存在性能问题,因为它们会多次调用 string::replace 从而生成大小与 blob 相同的副本。(我不知道 Boost 的解决方案,也许从这个角度来看是可行的)

此解决方案沿着源字符串中的所有出现并逐个构建新字符串。

void replaceAll(std::string& source, const std::string& from, const std::string& to)
{
    std::string newString;
    newString.reserve(source.length());  // avoids a few memory allocations

    std::string::size_type lastPos = 0;
    std::string::size_type findPos;

    while(std::string::npos != (findPos = source.find(from, lastPos)))
    {
        newString.append(source, lastPos, findPos - lastPos);
        newString += to;
        lastPos = findPos + from.length();
    }

    // Care for the rest after last occurrence
    newString += source.substr(lastPos);

    source.swap(newString);
}

这绝对是目前为止最佳的解决方案,它仅建立在STL上。如果你想为随时随地使用而添加一个自定义函数,那就用这个吧。 - Roger Sanders
3
最好使用newString.append(source, lastPos, source.length() - lastPos);而不是newString += source.substr(lastPos);以避免创建临时字符串[因为substr()会创建临时字符串]。(我无法编辑此答案,因为建议的编辑队列已满。) - tav
由于这个解决方案创建了一个临时字符串,如果内存受限,会不会有问题呢? - undefined
@tav:好想法,我猜那样会更好。实施者们,加油! - undefined
@thd:如果内存已经是一个问题,请不要使用它。我的解决方案可能不适合一对一字符替换,建议在可以原地操作的情况下使用其他方法。我的解决方案适用于替换片段比被替换片段更长的情况,这需要一个更大的缓冲区,因此内存开销大约是原始字符串的两倍。选择最适合你的方法吧。 - undefined

25

对于单个字符的简单查找和替换,可以采用以下方式:

s.replace(s.find("x"), 1, "y")

要对整个字符串执行此操作,最简单的方法是循环直到s.find开始返回npos。我想你也可以捕获range_error以退出循环,但那样有点丑陋。


9
当需要替换的字符数量相对于字符串长度较小时,这可能是一个适当的解决方案,但它不能很好地扩展。随着原始字符串中需要替换的字符比例增加,该方法将逐渐接近O(N^2)的时间复杂度。 - andand
7
正确,我的一般哲学是做容易(写和阅读)的事情,直到效率低下导致真正的问题出现。有些情况下,你可能会有巨大的字符串,其中O(N**2)很重要,但99%的时间我的字符串都在1K或更少。 - T.E.D.
4
话虽如此,我更喜欢Kirill的方法(并且已经顶了它)。 - T.E.D.
如果找不到"x"会发生什么?另外,为什么要使用双括号? - Prasath Govind
@PrasathGovind - 我只是展示所需的调用(因此是“类似于”)。 重要但模糊的细节,例如正确的错误处理留给读者自己练习。 至于“双括号”,我不确定那是什么,或者你在说什么。 对我来说,“括号”是{字符。 我不知道“双括号”是什么。 也许你有某种字体问题? - T.E.D.
我完全赞成不要过早优化,但避免O(N^2)并不是过早优化。 - Jeffrey Faust

19

为了完整起见,以下是使用std::regex进行操作的方法。

#include <regex>
#include <string>

int main()
{
    const std::string s = "example string";
    const std::string r = std::regex_replace(s, std::regex("x"), "y");
}

1
正则表达式替换的速度比Gauthier Boaglio的解决方案慢得多(慢10倍以上)... - David Constantine

7
如果您想替换的不止是单个字符,并且仅涉及 std::string,则此代码段将起作用,它可以将 sHaystack 中的 sNeedle 替换为 sReplace,而且 sNeedle 和 sReplace 的大小不需要相同。此例程使用 while 循环来替换所有出现次数,而不仅仅是从左到右找到第一个。
while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}

这是O(n^)。你可以在O(n)时间内完成它。 - Changming Sun
3
@ChangmingSun,你指的是哪个时间复杂度为 O(n) 的解法? - habakuk
4
如果kNeedle恰好是sReplace的子字符串,那么这将会进入无限循环。 - prideout
而且还有两次“find”调用。考虑将其结果制作为一个临时变量。 - Luc Bloom

4
如Kirill所建议的,可以使用replace方法或迭代字符串并独立替换每个字符。或者,您可以使用find方法或find_first_of方法,具体取决于您需要做什么。这些解决方案都无法一次完成工作,但是通过添加几行额外的代码,您应该能够使它们为您工作。 :-)

4

关于 Abseil StrReplaceAll 呢?从头文件来看:

// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
// std::string html_escaped = absl::StrReplaceAll(user_input, {
//                                                {"&", "&amp;"},
//                                                {"<", "&lt;"},
//                                                {">", "&gt;"},
//                                                {"\"", "&quot;"},
//                                                {"'", "&#39;"}});

3

老派的风格 :-)

std::string str = "H:/recursos/audio/youtube/libre/falta/"; 

for (int i = 0; i < str.size(); i++) {
    if (str[i] == '/') {
        str[i] = '\\';
    }
}

std::cout << str;

结果:

H:\资源\音频\YouTube\自由\缺少\


适用于ASCII的效果不错,但对于UTF8+来说就不太理想了。 - John

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