จะแทนที่อักขระทั้งหมดในสตริงได้อย่างไร?


คำตอบ:


742

std::stringไม่มีฟังก์ชันดังกล่าว แต่คุณสามารถใช้replaceฟังก์ชันสแตนด์อะโลนจากalgorithmส่วนหัว

#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'
}

6
std::stringเป็นคอนเทนเนอร์ที่ออกแบบมาเพื่อใช้งานกับลำดับของอักขระ ลิงก์
Kirill V. Lyadvinsky

164
น่าเสียดายที่สิ่งนี้อนุญาตให้แทนที่เพียงหนึ่งอักขระด้วยอักขระอื่น ไม่สามารถแทนที่อักขระด้วยอักขระอื่น ๆ (นั่นคือโดยสตริง) มีวิธีในการค้นหาแทนที่ด้วยตัวอักษรมากขึ้น?
SasQ

6
@ Kirill V. Lyadvinsky เกิดอะไรขึ้นถ้าฉันแค่อยากจะลบสิ่งที่เกิดขึ้น
SIFE

4
@ KirillV.Lyadvinsky: เมื่อฉันใช้วิธีนี้เพื่อแทนที่ x ทั้งหมดด้วย y's ผลลัพธ์จะเป็นสตริง y ที่มีความยาวไม่ว่าสตริงนั้นจะเป็นอะไรก็ตาม ฉันอยากรู้ว่าคุณคิดว่าจะเป็นปัญหาอะไร (รหัสตรงกับที่คุณเขียนทุกประการ)
Transcendent

6
@Transcendent: นี่คือสิ่งที่เกิดขึ้นกับstd::string::replace()แทนที่จะเป็นstd::replace()! 'x' ( char) ถูก castic โดยนัยถึงsize_t[ค่า 120] ดังนั้นสตริงทั้งหมดหรือบางส่วนจะเต็มไปด้วย 120 สำเนาของ 'y'
IBue

127

ฉันคิดว่าฉันจะโยนในโซลูชันเพิ่มเช่นกัน:

#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

ข้างต้นมีประสิทธิภาพมากขึ้นเพราะมันออกมาพร้อมกับ std lib.No ใช้ห้องสมุดเพิ่ม ;-)
hfrmobile

122

คำถามอยู่ตรงกลางของการcharacterแทนที่ แต่เมื่อฉันพบหน้านี้มีประโยชน์มาก (โดยเฉพาะหมายเหตุของKonrad ) ฉันต้องการแบ่งปันการใช้งานทั่วไปที่มากกว่านี้ซึ่งช่วยให้จัดการกับsubstrings:

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


แก้ไข:

ดังกล่าวข้างต้นสามารถดำเนินการในทางที่เหมาะสมมากขึ้นในการแสดงกรณีที่มีความกังวลของคุณโดยการกลับมาไม่มีอะไร ( void) และการแสดงการเปลี่ยนแปลงโดยตรงบนสตริงstrให้เป็นอาร์กิวเมนต์, ส่งตามที่อยู่แทนด้วยค่า สิ่งนี้จะหลีกเลี่ยงการคัดลอกสตริงต้นฉบับที่ไม่มีประโยชน์และมีค่าใช้จ่ายสูงขณะที่ส่งคืนผลลัพธ์ สายของคุณแล้ว ...

รหัส:

static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
    // Same inner code...
    // No return statement
}

หวังว่านี่จะเป็นประโยชน์สำหรับคนอื่น ๆ ...


4
อันนี้มีปัญหาประสิทธิภาพการทำงานในกรณีที่สายแหล่งที่มามีขนาดใหญ่และมีหลายสายที่จะถูกแทนที่ string :: replace () จะถูกเรียกหลายครั้งซึ่งทำให้เกิดการคัดลอกสตริงจำนวนมาก ดูโซลูชันของฉันที่จัดการปัญหานั้น
minastaros

1
Nit การเลือกข้างหน้า: ตามที่อยู่ => โดยอ้างอิง ไม่ว่าจะเป็นที่อยู่หรือไม่เป็นรายละเอียดการใช้งาน
Max Truxa

1
คุณควรตรวจสอบว่าfromสายอักขระว่างเปล่ามิฉะนั้นจะเกิดการวนซ้ำไม่รู้จบ
มือใหม่

34

ลองนึกภาพ Blob ไบนารีขนาดใหญ่ที่ 0x00 ไบต์ทั้งหมดจะถูกแทนที่ด้วย "\ 1 \ x30" และ 0x01 ไบต์ทั้งหมดด้วย "\ 1 \ x31" เพราะโปรโตคอลการขนส่งไม่อนุญาตให้ \ 0 ไบต์

ในกรณีที่:

  • สตริงการแทนที่และสตริงการแทนที่มีความยาวต่างกัน
  • มีเหตุการณ์จำนวนมากของสตริงที่จะถูกแทนที่ภายในสตริงต้นฉบับและ
  • สตริงต้นทางมีขนาดใหญ่

ไม่สามารถใช้โซลูชันที่ให้มาได้ (เพราะพวกเขาแทนที่ตัวอักษรเดียวเท่านั้น) หรือมีปัญหาด้านประสิทธิภาพเนื่องจากพวกเขาจะเรียก string :: แทนที่หลาย ๆ ครั้งซึ่งสร้างสำเนาขนาดของหยดขึ้นไป (ฉันไม่ทราบวิธีเพิ่มประสิทธิภาพบางทีมันก็โอเคจากมุมมองนั้น)

สิ่งนี้เดินตามเหตุการณ์ที่เกิดขึ้นทั้งหมดในสตริงต้นฉบับและสร้างสตริงสตริงใหม่ทีละชิ้น:

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

21

การค้นหาและแทนที่อย่างง่ายสำหรับตัวละครตัวหนึ่งจะเป็นอย่างไร:

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

การทำเช่นนี้สำหรับสตริงทั้งสิ่งที่ง่ายที่จะทำจะเป็นห่วงจนกว่าคุณจะเริ่มต้นกลับมาs.find nposฉันคิดว่าคุณสามารถจับrange_errorเพื่อออกจากลูปได้ แต่มันก็น่าเกลียด


7
แม้ว่านี่อาจเป็นทางออกที่เหมาะสมเมื่อจำนวนตัวอักษรที่จะเปลี่ยนมีขนาดเล็กเมื่อเทียบกับความยาวของสตริง เนื่องจากสัดส่วนของตัวละครในสตริงเดิมที่จำเป็นต้องเพิ่มขึ้นแทนที่วิธีนี้จะเข้าใกล้ O (N ^ 2) ในเวลา
andand

7
จริง ปรัชญาทั่วไปของฉันคือการทำสิ่งที่ง่าย (เขียนและอ่าน) จนกว่าจะถึงเวลาที่ความไร้ประสิทธิภาพก่อให้เกิดปัญหาจริง มีบางสถานการณ์ที่คุณอาจมีสตริงที่ไม่น่าเชื่อที่ O (N ** 2) มีความสำคัญ แต่ 99% ของเวลาที่สตริงของฉันคือ 1K หรือน้อยกว่า
TED

3
... ที่ถูกกล่าวว่าฉันชอบวิธีของคิริลล์ดีกว่า (และโหวตให้มันแล้ว)
TED

จะเกิดอะไรขึ้นหากไม่พบ "x" นอกจากนี้ทำไมคุณถึงใช้เครื่องหมายปีกกาคู่
Prasath Govind

@rasathgovind - ฉันแค่แสดงสายที่ต้องการ (ดังนั้น "บางอย่างเช่น") สำคัญ แต่รายละเอียดที่ไม่ชัดเจนเช่นการจัดการข้อผิดพลาดที่เหมาะสมถูกทิ้งไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน สำหรับ "วงเล็บปีกกาคู่" ฉันไม่แน่ใจว่าสิ่งเหล่านั้นคืออะไรหรือสิ่งที่คุณกำลังพูดถึง สำหรับฉัน "รั้ง" เป็น{ตัวละคร ฉันไม่รู้ว่า "รั้งสองครั้ง" คืออะไร บางทีคุณอาจมีปัญหาเกี่ยวกับแบบอักษรบ้าง
TED

6

หากคุณต้องการแทนที่อักขระมากกว่าหนึ่งตัวและกำลังจัดการกับข้อมูลเท่านั้นstd::stringตัวอย่างข้อมูลนี้จะทำงานแทนที่ sNeedle ใน sHaystack ด้วย 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

2
นี่จะวนซ้ำไม่สิ้นสุดหาก kNeedle เกิดขึ้นเป็นสตริงย่อยของ sReplace
ภูมิใจ

รวมทั้งมีการfindโทรสองครั้ง ลองพิจารณาทำให้ผลลัพธ์นั้นเป็นตัวแปรชั่วคราว
Luc Bloom

4

ตามที่คิริลล์แนะนำให้ใช้วิธีการแทนที่หรือวนซ้ำตามสตริงเพื่อแทนที่ถ่านแต่ละตัวโดยอิสระ

หรือคุณสามารถใช้findวิธีการหรือfind_first_ofขึ้นอยู่กับสิ่งที่คุณต้องทำ ไม่มีวิธีแก้ปัญหาใดที่จะทำงานได้ในคราวเดียว แต่ด้วยโค้ดเพิ่มเติมอีกสองสามบรรทัดคุณควรทำให้มันทำงานให้คุณได้ :-)


3
#include <iostream>
#include <string>
using namespace std;
// Replace function..
string replace(string word, string target, string replacement){
    int len, loop=0;
    string nword="", let;
    len=word.length();
    len--;
    while(loop<=len){
        let=word.substr(loop, 1);
        if(let==target){
            nword=nword+replacement;
        }else{
            nword=nword+let;
        }
        loop++;
    }
    return nword;

}
//Main..
int main() {
  string word;
  cout<<"Enter Word: ";
  cin>>word;
  cout<<replace(word, "x", "y")<<endl;
  return 0;
}

หากwordยาวอาจมีโอเวอร์เฮดจำนวนมากขณะเรียกใช้ฟังก์ชัน คุณสามารถเพิ่มประสิทธิภาพนี้โดยผ่านword, targetและreplacementเป็น const อ้างอิง
TrebledJ

2

แล้ว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;"}});

1

โรงเรียนเก่า :-)

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: \ Recursos \ เสียง \ YouTube \ ฟรี \ falta \


0

มันใช้งานได้! ฉันใช้สิ่งที่คล้ายกับสิ่งนี้สำหรับแอปหนังสือที่เก็บสินค้าคงคลังในรูปแบบ CSV (เช่นไฟล์. dat) แต่ในกรณีของถ่านตัวเดียวหมายความว่าตัวจำลองเป็นเพียงตัวเดียวเช่น '|' จะต้องอยู่ในเครื่องหมายคำพูดคู่ "|" เพื่อไม่ให้เกิดการแปลง const char ที่ไม่ถูกต้อง

#include <iostream>
#include <string>

using namespace std;

int main()
{
    int count = 0;  // for the number of occurences.
    // final hold variable of corrected word up to the npos=j
    string holdWord = "";
    // a temp var in order to replace 0 to new npos
    string holdTemp = "";
    // a csv for a an entry in a book store
    string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";

    // j = npos
    for (int j = 0; j < holdLetter.length(); j++) {

        if (holdLetter[j] == ',') {

            if ( count == 0 ) 
            {           
                holdWord = holdLetter.replace(j, 1, " | ");      
            }
            else {

                string holdTemp1 = holdLetter.replace(j, 1, " | ");

                // since replacement is three positions in length,
                // must replace new replacement's 0 to npos-3, with
                // the 0 to npos - 3 of the old replacement 
                holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3); 

                holdWord = "";

                holdWord = holdTemp;

            }
            holdTemp = "";
            count++;
        }
    } 
    cout << holdWord << endl;
    return 0;
}

// result:
Big Java 7th Ed | Horstman | 978-1118431115 | 99.85

ตอนนี้ฉันใช้ CentOS อยู่แล้วดังนั้นรุ่นคอมไพเลอร์ของฉันจึงอยู่ด้านล่าง รุ่น C ++ (g ++), C ++ 98 เริ่มต้น:

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0

หากคุณยินดีที่จะใช้std::stringคุณสามารถใช้strsubฟังก์ชั่นตัวอย่างของแอพนี้หรืออัปเดตหากคุณต้องการให้ใช้พารามิเตอร์ประเภทอื่นหรือชุดพารามิเตอร์เพื่อให้บรรลุเป้าหมายเดียวกันโดยประมาณ โดยทั่วไปจะใช้คุณสมบัติและฟังก์ชันการทำงานของได้อย่างรวดเร็วลบชุดการจับคู่ของตัวอักษรและแทรกตัวอักษรที่ต้องการโดยตรงภายในstd::string std::stringทุกครั้งที่มีการดำเนินการเปลี่ยนนี้ offset จะอัปเดตหากยังคงสามารถค้นหาตัวอักษรที่ตรงกันเพื่อแทนที่และหากไม่สามารถแทนที่ได้อีกต่อไปจะส่งคืนสตริงในสถานะจากการอัพเดตล่าสุด

#include <iostream>
#include <string>

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "yyy", "i");
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "ii", "y");

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, charsToReplace.size());
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
                                           this_occurrence + replacementChars.size());
    }

    return this_string;
}

หากคุณไม่ต้องการพึ่งพาการใช้std::strings เป็นพารามิเตอร์ของคุณเพื่อให้คุณสามารถส่งผ่านสตริง C-style แทนคุณสามารถดูตัวอย่างที่อัปเดตด้านล่าง:

#include <iostream>
#include <string>

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "yyy", "i", 3, 1);
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "ii", "y", 2, 1);

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, sizeOfCharsToReplace);
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
            this_occurrence + sizeOfReplacementChars);
    }

    return this_string;
}

0

สำหรับสถานการณ์ง่าย ๆ มันใช้งานได้ค่อนข้างดีโดยไม่ต้องใช้ไลบรารี่ตัวอื่นจากนั้น std :: string (ซึ่งใช้งานอยู่แล้ว)

แทนที่อักขระa ทั้งหมดด้วยอักขระbในsome_string :

for (size_t i = 0; i < some_string.size(); ++i) {
    if (some_string[i] == 'a') {
        some_string.replace(i, 1, "b");
    }
}

หากสตริงมีขนาดใหญ่หรือโทรซ้ำเพื่อแทนที่เป็นปัญหาคุณสามารถใช้เทคนิคที่กล่าวถึงในคำตอบนี้: https://stackoverflow.com/a/29752943/3622300


0

นี่คือทางออกที่ฉันรีดด้วยจิตวิญญาณ DRI สูงสุด มันจะค้นหา sNeedle ใน sHaystack และแทนที่ด้วย sReplace, nTimes ถ้าไม่ใช่ 0, อื่น ๆ ทั้งหมดที่เกิดขึ้น sNeedle มันจะไม่ค้นหาอีกครั้งในข้อความที่ถูกแทนที่

std::string str_replace(
    std::string sHaystack, std::string sNeedle, std::string sReplace, 
    size_t nTimes=0)
{
    size_t found = 0, pos = 0, c = 0;
    size_t len = sNeedle.size();
    size_t replen = sReplace.size();
    std::string input(sHaystack);

    do {
        found = input.find(sNeedle, pos);
        if (found == std::string::npos) {
            break;
        }
        input.replace(found, len, sReplace);
        pos = found + replen;
        ++c;
    } while(!nTimes || c < nTimes);

    return input;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.