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

Posted

技术标签:

【中文标题】用另一个字符串替换一个字符串的一部分【英文标题】:Replace part of a string with another string 【发布时间】:2011-03-25 23:56:26 【问题描述】:

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

基本上,我想这样做:

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

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

【问题讨论】:

What is the function to replace string in C? 的可能重复项——糟糕,抱歉,这是 C,而不是 C++;我希望我可以取消投票。 @poly 我想这肯定也被要求用于 C++,但我找不到它 问题上有一个std标签,但也许你可能对boost的字符串算法感兴趣,它还包括多种替换算法(就地/复制,区分大小写/不区分大小写,先替换/last/all/n-th)。 @Michael Mrozek ***.com/questions/3418231/… 有一个,但它更新了,你的 replaceAll 方法更健壮。 How do I Search/Find and Replace in a standard string?的可能重复 【参考方案1】:

这可能会更好用

void replace(string& input, const string& from, const string& to)

    auto pos = 0;
    while(true)
    
        size_t startPosition = input.find(from, pos);
        if(startPosition == string::npos)
            return;
        input.replace(startPosition, from.length(), to);
        pos += to.length();
    

【讨论】:

string s = "ha"; replace(s, "h", "uhoh"); 而不是发表讽刺评论@JonathanWakely,您实际上可以明确地描述您在评论中暗示的问题 @v2v1 这不是冷嘲热讽,我也不是在暗示,我是在演示。教人钓鱼,诸如此类。找出函数不起作用的原因是一个有用的练习。如果你想有一个评论来解释它,可以添加一个。我注意到你也没有这样做,你宁愿批评我的评论。 @JonathanWakely 使用“uhoh”和“ha”是对我的讽刺。当然,“演示”并强迫阅读本文的人运行无限循环?为什么不直接告诉他们?这不像他们会通过自己炸毁代码来获得任何东西。而且解释也很简单,不如换个说法。【参考方案2】:

您可以使用此代码删除子字符串,也可以替换,还可以删除多余的空格。 代码:

#include<bits/stdc++.h>
using namespace std ;
void removeSpaces(string &str)

   
    int n = str.length();

    int i = 0, j = -1;

    bool spaceFound = false;

    while (++j <= n && str[j] == ' ');

    while (j <= n)
    
        if (str[j] != ' ')
        
          
            if ((str[j] == '.' || str[j] == ',' ||
                 str[j] == '?') && i - 1 >= 0 &&
                 str[i - 1] == ' ')
                str[i - 1] = str[j++];

            else
                
                str[i++] = str[j++];

            
            spaceFound = false;
        
        else if (str[j++] == ' ')
        
            
            if (!spaceFound)
            
                str[i++] = ' ';
                spaceFound = true;
            
        
    

    if (i <= 1)
        str.erase(str.begin() + i, str.end());
    else
        str.erase(str.begin() + i - 1, str.end());

int main()

    string s;
    cin>>s;
    for(int i=s.find("WUB");i>=0;i=s.find("WUB"))
    
        s.replace(i,3," ");
    
    removeSpaces(s);
    cout<<s<<endl;

    return 0;

【讨论】:

这将 "WUB" 替换为 " " 但这不是 OP 所要求的,也不是删除空格(您的大多数答案都是这样做的)。如果替换也与要替换的字符串匹配,则查找/替换解决方案不起作用。【参考方案3】:

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

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

转义转义字符需要双反斜杠。

【讨论】:

我很确定 std::regex_replace 不接受 Qt 的字符串。 你是对的。碰巧QString 提供了一个replace 方法,它接受一个QRexExp,允许使用Qt 自己的东西。但我认为可以通过将string 替换为string.toStdString() 来纠正当前的答案。 或者只是将String改为std::string,因为这个问题与Qt无关。请考虑这样做 - 之后我很乐意为您的答案投票。 原始字符串允许写入R"(\$name)" 而不是"\\$name" 如果不考虑std::regex的构造时间,这会比查找/替换慢多少?【参考方案4】:

std::string 有一个replace 方法,这就是你要找的吗?

你可以试试:

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

我自己没试过,只是阅读find()replace()上的文档。

【讨论】:

据我所知,std::string 替换方法并没有像我想要的那样使用两个字符串。 这对我不起作用。 sizeof 应替换为 string("Somename").size()-1 @TimZaman:这让我很困惑,文档清楚地说明你可以从 C 风格的字符串初始化。 第二个参数应该是“$name”的长度(而不是“Somename”的长度),不是吗? 如果"$name"s 字符串中多次出现,这将不起作用。【参考方案5】:

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

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(), "\\", "/"), ":", "");

【讨论】:

【参考方案6】:

如果您想快速完成,可以使用两次扫描方法。 伪代码:

    第一次解析。找出有多少匹配的字符。 扩展字符串的长度。 第二次解析。当我们得到替换的匹配项时,从字符串的末尾开始,否则我们只需从第一个字符串中复制字符。

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

还有一个 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";


【讨论】:

【参考方案7】:

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

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

这取代了 sHaystack inline -- 无需对其进行 = 赋值。

示例用法:

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;

【讨论】:

【参考方案8】:
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);

【讨论】:

【参考方案9】:

我刚刚在学习 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;

【讨论】:

【参考方案10】:

我一般用这个:

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() 返回匹配的位置,所以我们不存在使迭代器失效的问题。

【讨论】:

【参考方案11】:

这听起来像是一个选项

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

您可以将其包装在一个函数中,但这种单行解决方案听起来可以接受。 问题是这只会改变第一次出现,你可能想循环它,但它也允许你用相同的标记将几个变量插入到这个字符串中 (%s)

【讨论】:

喜欢风格但发现不同的字符串令人困惑^^ str.replace(str.find("%s"), string("%s").size(), "Something");【参考方案12】:
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;

【讨论】:

您能否解释一下这段代码(在您的回答中)?这样你可能会得到更多的支持!【参考方案13】:

有一个函数可以在字符串中查找子字符串 (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'
    

【讨论】:

如果原始字符串有多个“$name”实例并且我想替换所有实例,我将如何解决它。 为什么 fromto 不通过 const 引用?如果from 不存在,你的功能是什么? -1 来自我。 @sbi 已修复,尽管您可以将其表述为建议而不是攻击——我根本没有想到,我很少考虑使用const,如果我写了一个实用方法像这样,如果我知道替换是有效的,我只会调用它 @Michael:很好,我把我的反对票变成了赞成票。解雇const 是在无视C++ 最好的工具之一。 Passing per const reference should be the default mode for function parameters.(FTR,没有const,您甚至无法将字符串文字传递给您的函数,因为您无法将临时对象绑定到非const 引用。因此该函数甚至不会执行写入的操作.) 这仍然是2018年唯一的解决方案吗?如果是这样并且任何 C++ 委员会正在阅读此内容,请将其整理出来。太丢脸了。请拆分(字符串,字符串)和替换(字符串,字符串)!【参考方案14】:

要返回新字符串,请使用:

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,我认为它确实会“就地”发生,或者没有任何复制,但是如果您扩展字符串,只有旧字符串的第一部分不会被复制,只有那时才可能。【参考方案15】:

是的,你可以这样做,但是你必须用字符串的 find() 成员找到第一个字符串的位置,然后用它的 replace() 成员替换。

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

如果您打算使用标准库,那么您真的应该得到一本 The C++ Standard Library 这本书的副本,它很好地涵盖了所有这些内容。

【讨论】:

它是 size_t 而不是 size_type 它是 std::string::size_type,而不是 size_t 或朴素的 size_type。

以上是关于用另一个字符串替换一个字符串的一部分的主要内容,如果未能解决你的问题,请参考以下文章

用另一个字符替换字符串中的某些字符

用另一个替换特定字符串 - String#replaceAll()

为啥我们在尝试用另一个字符串替换它时使用带有“\”的@ [重复]

用另一个元素替换字符串中的元素的函数[重复]

pandas:用另一个字符串替换字符串

Ruby - 用另一个字符串替换第一次出现的子字符串