แทนที่ส่วนหนึ่งของสตริงด้วยสตริงอื่น


186

เป็นไปได้ใน C ++ เพื่อแทนที่ส่วนหนึ่งของสตริงด้วยสตริงอื่น?

โดยทั่วไปฉันต้องการทำสิ่งนี้:

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

แต่ฉันต้องการใช้ไลบรารี C ++ มาตรฐาน


1
เป็นไปได้ที่ซ้ำกันของฟังก์ชั่นเพื่อแทนที่สตริงใน C คืออะไร? - อุ๊ปส์ขออภัยนั่นคือ C ไม่ใช่ C ++ ฉันหวังว่าฉันจะสามารถยกเลิกการลงคะแนน
polygenelubricants

1
@poly ฉันคิดว่ามันต้องถูกถาม C ++ เหมือนกัน แต่ฉันไม่สามารถหามันได้
Michael Mrozek

1
มีแท็ก std อยู่ในคำถาม แต่บางทีคุณอาจสนใจในอัลกอริธึมสตริงของบูสต์ซึ่งรวมถึงตัวเลือกแทนที่อัลกอริทึม (inplace / copy, case sensitive / case insensitive, แทนที่ first / last / all / n-th )
ลุง

@Michael Mrozek มีมากกว่าหนึ่งที่stackoverflow.com/questions/3418231/…แต่มันใหม่กว่าและวิธีการ replaceAll ของคุณทั้งหมดที่นี่มีประสิทธิภาพมากกว่า
dave-holm

คำตอบ:


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" มากกว่าหนึ่งอันและฉันต้องการแทนที่พวกเขาทั้งหมด
Tom Leese

1
เหตุใดจึงไม่fromและtoผ่านตามconstการอ้างอิง หน้าที่ของคุณคืออะไรถ้าfromไม่มี -1จากฉันสำหรับสิ่งนั้น
sbi

10
@sbi Fixed แม้ว่าคุณจะสามารถใช้ถ้อยคำเป็นคำแนะนำแทนการโจมตี - มันก็ไม่ได้เกิดขึ้นกับฉันฉันคิดว่าจะไม่ค่อยใช้constและถ้าฉันเขียนวิธีการใช้งานยูทิลิตี้แบบนี้ฉันจะเรียกมันถ้าฉันรู้ การแทนที่ถูกต้อง
Michael Mrozek

10
@Michael: ดีฉันเปลี่ยนการลงคะแนนของฉันเป็นการลงคะแนน กำลังเลิกใช้งานconstกำลังมองข้ามหนึ่งในเครื่องมือที่ดีที่สุดของ C ++ การส่งต่อconstการอ้างอิงควรเป็นโหมดเริ่มต้นสำหรับพารามิเตอร์ฟังก์ชัน (FTR หากไม่มีconstคุณจะไม่สามารถส่งค่าตัวอักษรสตริงไปยังฟังก์ชันของคุณได้เพราะคุณไม่สามารถผูกค่าขมับให้กับconstการอ้างอิงไม่ได้ดังนั้นฟังก์ชั่นจะไม่ทำสิ่งที่เขียนไป)
sbi

19
นี่ยังคงเป็นทางออกเดียวในปี 2561? ถ้าเป็นเช่นนั้นและคณะกรรมการ C ++ กำลังอ่านข้อความนี้ให้เรียงลำดับ มันน่าอาย. แยก (สตริง, สตริง) และแทนที่ (สตริง, สตริง) โปรด!
user997112

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

1
คุณพูดถูก ในขณะที่มันเกิดขึ้น QString มีวิธีการแทนที่ซึ่งยอมรับ QRexExp ทำให้สามารถใช้สิ่งของของ Qt ได้ แต่ผมคิดว่าคำตอบปัจจุบันอาจได้รับการแก้ไขโดยการแทนที่ด้วยstring string.toStdString()
Tom

1
หรือเพียงแค่เปลี่ยนStringเป็นstd::stringเพราะคำถามไม่เกี่ยวข้องกับ Qt โปรดลองทำอย่างนั้น - ฉันยินดีที่จะคัดค้านคำตอบของคุณในภายหลัง
BartoszKP

5
สตริงดิบจะช่วยให้การเขียนแทนR"(\$name)" "\\$name"
Jarod42

2
สิ่งนี้จะช้ากว่าการค้นหา / แทนที่โดยไม่คำนึงถึงเวลาการสร้างของ std :: regex?
jw_

18

std::stringมีreplaceวิธีคือสิ่งที่คุณกำลังมองหา?

คุณสามารถลอง:

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

ฉันไม่ได้พยายามตัวเองเพียงแค่อ่านเอกสารบนและfind()replace()


2
จากสิ่งที่ฉันเห็น std :: string replace method ไม่ได้ใช้สองสายตามที่ฉันต้องการ
Tom Leese

2
มันใช้งานไม่ได้สำหรับฉัน sizeof ควรถูกแทนที่ด้วยสตริง ("Somename") ขนาด () - 1
TimZaman

@ TimZaman: นั่นเป็นปริศนาให้ฉันเอกสารที่ระบุอย่างชัดเจนว่าคุณสามารถเริ่มต้นจากสตริงแบบ C
SC Madsen

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

การเรียกไปยัง subject.replace ใน ReplaceStringInPlace () นั้นเป็นการแก้ไขสตริงภายในหรือไม่?
เดเมียน

ฉันดูที่แหล่งข้อมูลสั้น ๆ และดูเหมือนว่าใช้ความหมายของการย้ายเพื่อย้ายด้านหน้าของสตริงเก่าเข้าที่ดังนั้นจึงไม่ถูกคัดลอก แต่ส่วนใหม่ที่แทรกเข้าไปจะถูกคัดลอกลงในสตริงเก่าและส่วนท้ายของสตริงเก่า ถูกคัดลอกไปยังบัฟเฟอร์ที่ปรับขนาดแล้วของสตริงเก่า มีความเป็นไปได้ที่สตริงจะขยายจำนวนบัฟเฟอร์ที่จัดสรรใหม่ทั้งหมด แต่ถ้าคุณแทนที่ 1 ต่อ 1 ดังตัวอย่างของเขาฉันคิดว่ามันเกิดขึ้น "แทนที่" หรือไม่มีการคัดลอกใด ๆ แต่ถ้าคุณขยายสตริง เฉพาะส่วนแรกของสตริงเก่าเท่านั้นที่ไม่ถูกคัดลอกและอาจเป็นไปได้
Motes

6

ใช่คุณสามารถทำได้ แต่คุณต้องค้นหาตำแหน่งของสตริงแรกที่มีสมาชิก find () ของสตริงแล้วแทนที่ด้วยมันแทนที่ () สมาชิก

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ซึ่งครอบคลุมเนื้อหาทั้งหมดนี้เป็นอย่างดี


1
มันคือ size_t และไม่ใช่ size_type
revo

1
มันเป็นมาตรฐาน :: สตริง :: 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");
Paul Würtz

3

หากสตริงทั้งหมดเป็น std :: string คุณจะพบปัญหาแปลก ๆ กับการตัดยอดอักขระหากใช้sizeof()เพราะมันมีไว้สำหรับสตริง C ไม่ใช่สตริง C ++ การแก้ไขคือการใช้วิธีการเรียนของ.size()std::string

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;

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
คุณกรุณาอธิบายรหัสนี้ (ในคำตอบของคุณ) ได้ไหม? คุณอาจได้รับ upvotes มากกว่านี้!
ผู้ชายกับหมวก

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