Ganti bagian dari string dengan string lain

186

Apakah mungkin dalam C ++ untuk mengganti bagian dari string dengan string lain?

Pada dasarnya, saya ingin melakukan ini:

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

Tapi saya ingin menggunakan pustaka C ++ standar.

Tom Leese
sumber
1
kemungkinan duplikat Apa fungsi untuk mengganti string dalam C? - oops maaf itu C, bukan C ++; Saya berharap saya dapat membatalkan.
polygenelubricants
1
@poly Saya akan berpikir ini pasti telah diminta untuk C ++ juga, tapi saya tidak dapat menemukannya
Michael Mrozek
1
Ada tag std pada pertanyaan, tetapi mungkin Anda mungkin tertarik pada algoritma string boost, yang juga mencakup berbagai pilihan algoritma ganti (inplace / copy, case-sensitive / case-sensitive, ganti first / last / all / n-th) ).
UncleBens
@Michael Mrozek Ada satu di stackoverflow.com/questions/3418231/... tapi ini lebih baru dan metode replaceAll Anda di sini lebih kuat.
dave-holm

Jawaban:

288

Ada fungsi untuk menemukan substring dalam string ( find), dan fungsi untuk mengganti rentang tertentu dalam string dengan string lain ( replace), sehingga Anda bisa menggabungkan mereka untuk mendapatkan efek yang Anda inginkan:

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");

Menanggapi komentar, saya pikir replaceAllmungkin akan terlihat seperti ini:

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'
    }
}
Michael Mrozek
sumber
2
Bagaimana saya memperbaikinya jika string asli memiliki lebih dari satu contoh "$ name" dan saya ingin mengganti semuanya.
Tom Leese
1
Mengapa tidak fromdan tolulus per constreferensi? Apa fungsi Anda jika fromtidak ada? -1dari saya untuk itu.
sbi
10
@ sbi Memperbaiki, walaupun Anda bisa mengartikannya sebagai rekomendasi alih-alih serangan - itu tidak terpikir oleh saya, saya jarang berpikir untuk menggunakan constdan jika saya menulis metode utilitas seperti ini saya hanya akan menyebutnya jika saya tahu penggantian itu valid
Michael Mrozek
10
@Michael: Bagus, saya mengubah suara saya menjadi suara-up. Mengabaikan constmengabaikan salah satu alat terbaik C ++. Lulus per constreferensi harus menjadi mode default untuk parameter fungsi. (FTR, tanpa const, Anda bahkan tidak bisa meneruskan string literal ke fungsi Anda, karena Anda tidak dapat mengikat sementara ke non- constreferensi. Jadi fungsi tersebut bahkan tidak akan melakukan apa yang ditulis untuk.)
sbi
19
Apakah ini masih satu-satunya solusi di tahun 2018? Jika demikian dan setiap komite C ++ sedang membaca ini, aturlah. Ini memalukan. split (string, string) dan ganti (string, string) tolong!
user997112
96

Dengan C ++ 11 Anda dapat menggunakan std::regexseperti ini:

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

Double backslash diperlukan untuk melarikan diri dari karakter pelarian.

Tom
sumber
Saya cukup yakin std::regex_replacetidak menerima string Qt.
BartoszKP
1
Kamu benar. Ketika itu terjadi, QString menyediakan metode pengganti yang menerima QRexExp, memungkinkan untuk menggunakan barang-barang Qt sendiri. Tapi saya pikir jawaban saat ini bisa diperbaiki dengan mengganti stringdengan string.toStdString().
Tom
1
Atau hanya dengan mengubah Stringke std::string, karena pertanyaannya tidak terkait dengan Qt. Silakan pertimbangkan untuk melakukan itu - Saya dengan senang hati akan meningkatkan jawaban Anda setelahnya.
BartoszKP
5
String mentah memungkinkan untuk menulis R"(\$name)"bukan "\\$name".
Jarod42
2
Berapa ini akan lebih lambat daripada menemukan / mengganti tanpa mempertimbangkan waktu membangun std :: regex?
jw_
18

std::string mempunyai sebuah replace metode, apakah itu yang Anda cari?

Kamu bisa mencoba:

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

Saya belum mencoba sendiri, cukup baca dokumentasi find()dan replace().

SC Madsen
sumber
2
Dari apa yang saya lihat metode ganti string std :: string tidak membutuhkan dua string seperti yang saya inginkan.
Tom Leese
2
Ini tidak bekerja untuk saya. sizeof harus diganti dengan string ("Somename"). size () - 1
TimZaman
@TimZaman: Itu membingungkan saya, dokumentasi dengan jelas menyatakan Anda dapat menginisialisasi dari string C-style.
SC Madsen
5
argumen kedua haruslah panjang "$ name" (bukan panjang "Somename"), bukan?
Daniel Kiss
10

Untuk mengembalikan string baru, gunakan ini:

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;
}

Jika Anda membutuhkan kinerja, berikut ini adalah fungsi yang dioptimalkan yang memodifikasi string input, ini tidak membuat salinan string:

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();
    }
}

Tes:

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;

Keluaran:

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
Czarek Tomczak
sumber
Panggilan Anda ke subject.replace di ReplaceStringInPlace () apakah itu benar-benar memodifikasi string di tempat?
Damian
Saya melihat sumbernya sebentar dan sepertinya menggunakan semantik untuk memindahkan bagian depan string lama ke tempatnya, sehingga tidak disalin, tetapi bagian yang baru dimasukkan akan disalin ke string yang lama dan ekor dari string yang lama. disalin ke dalam buffered dari string lama. Ada kemungkinan bahwa string melebar begitu banyak seluruh buffer yang mendasarinya dialokasikan kembali, tetapi jika Anda mengganti 1 ke 1 seperti dalam contohnya, saya pikir itu terjadi "di tempat", atau tanpa menyalin, tetapi jika Anda memperluas string, hanya bagian pertama dari string lama yang tidak disalin, dan hanya mungkin kemudian.
Motes
6

Ya, Anda dapat melakukannya, tetapi Anda harus menemukan posisi string pertama dengan anggota find (), dan kemudian ganti dengan anggota replace ().

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

Jika Anda berencana menggunakan Perpustakaan Standar, Anda harus benar-benar mendapatkan salinan buku Perpustakaan C ++ Standar yang mencakup semua hal ini dengan sangat baik.

Drew Noakes
sumber
1
itu size_t dan bukan size_type
revo
1
Ini std :: string :: size_type, bukan size_t atau size_type tanpa hiasan.
jmucchiello
5

Saya menggunakan ini secara umum:

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;
}

Berulang kali panggilan std::string::find()untuk mencari kejadian lain dari string yang dicari sampai std::string::find()tidak menemukan apa pun. Karena std::string::find()mengembalikan posisi pertandingan, kami tidak memiliki masalah untuk membatalkan iterator.

Galik
sumber
4

Ini terdengar seperti opsi

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

Anda bisa membungkus ini dalam suatu fungsi tetapi solusi satu baris ini terdengar dapat diterima. Masalahnya adalah ini akan mengubah kejadian pertama saja, Anda mungkin ingin mengulanginya, tetapi juga memungkinkan Anda untuk memasukkan beberapa variabel ke string ini dengan token yang sama ( %s)

maksimumime
sumber
1
Menyukai gaya tetapi menemukan string yang berbeda membingungkan ^^str.replace(str.find("%s"), string("%s").size(), "Something");
Paul Würtz
3

Jika semua string std :: string, Anda akan menemukan masalah aneh dengan cutoff karakter jika menggunakan sizeof()karena itu dimaksudkan untuk string C, bukan string C ++. Cara mengatasinya adalah dengan menggunakan .size()metode kelas std::string.

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

Itu menggantikan inline sHaystack - tidak perlu melakukan tugas = kembali itu.

Contoh penggunaan:

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;
Volomike
sumber
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);
pengguna3016543
sumber
2

Jika Anda ingin melakukannya dengan cepat, Anda dapat menggunakan pendekatan dua pemindaian. Kode palsu:

  1. parse pertama. temukan berapa banyak karakter yang cocok.
  2. perluas panjang tali.
  3. parse kedua. Mulai dari akhir string ketika kita mendapatkan kecocokan yang kita ganti, kalau tidak kita hanya menyalin karakter dari string pertama.

Saya tidak yakin apakah ini dapat dioptimalkan ke algo di tempat.

Dan contoh kode C ++ 11 tapi saya hanya mencari satu karakter.

#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";

}
Damian
sumber
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;
}
Lucas Civali
sumber
2
Bisakah Anda jelaskan kode ini (dalam jawaban Anda)? Anda mungkin mendapatkan lebih banyak suara dengan cara itu!
The Guy with The Hat
1

Implementasi saya sendiri, dengan mempertimbangkan bahwa string perlu diubah ukuran hanya sekali, maka ganti bisa terjadi.

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;
}

Penggunaan bisa misalnya seperti ini:

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

Saya baru saja belajar C ++, tetapi mengedit beberapa kode yang sebelumnya diposting, saya mungkin akan menggunakan sesuatu seperti ini. Ini memberi Anda fleksibilitas untuk mengganti 1 atau beberapa instance, dan juga memungkinkan Anda menentukan titik awal.

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;
}
seorang programmer
sumber
0

Ini bisa lebih baik untuk digunakan

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);
    }
}
Yashwanth Kumar
sumber