Bagaimana cara mengganti semua kemunculan karakter dalam string?

480

Apa cara efektif untuk mengganti semua kemunculan karakter dengan karakter lain std::string?

besar-z
sumber

Jawaban:

742

std::stringtidak mengandung fungsi seperti itu tetapi Anda bisa menggunakan replacefungsi yang berdiri sendiri dari algorithmheader.

#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'
}
Kirill V. Lyadvinsky
sumber
6
std::stringadalah wadah yang dirancang khusus untuk beroperasi dengan urutan karakter. tautan
Kirill V. Lyadvinsky
164
Sayangnya, ini memungkinkan untuk mengganti hanya satu char dengan char yang lain. Itu tidak dapat mengganti char dengan lebih banyak karakter (yaitu dengan string). Apakah ada cara untuk melakukan pencarian-ganti dengan lebih banyak karakter?
SasQ
6
@ Kirill V. Lyadvinsky Bagaimana Jika Saya hanya ingin menghapus suatu kejadian.
SIFE
4
@ KirillV.Lyadvinsky: Ketika saya menggunakan metode ini untuk mengganti semua x dengan y, hasilnya adalah string y yang panjang tidak peduli apa string aslinya. Saya ingin tahu apa yang menurut Anda akan menjadi masalah. (kode ini persis sama dengan yang Anda tulis)
Transenden
6
@ Transcendent: Inilah tepatnya yang terjadi dengan std::string::replace()alih - alih std::replace()! 'x' ( char) secara tidak langsung dilemparkan ke size_t[nilai 120], sehingga seluruh string atau atau bagian dari itu akan diisi dengan 120 salinan 'y'.
IBue
127

Saya pikir saya juga akan melemparkan solusi penambah:

#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, "#", "@");
UncleZeiv
sumber
Kemudian Anda kehilangan beberapa -Iflag untuk kompiler Anda agar bisa menemukan pustaka Boost di sistem Anda. Mungkin Anda harus menginstalnya terlebih dahulu.
Martin Ueding
Di atas lebih efektif karena keluar dengan std lib. Tidak semua menggunakan boost library ;-)
hfrmobile
122

Pertanyaannya berpusat pada characterpenggantian, tetapi, karena saya menemukan halaman ini sangat berguna (terutama komentar Konrad ), saya ingin berbagi implementasi yang lebih umum ini, yang memungkinkan untuk menangani substringsjuga:

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

Pemakaian:

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;

Output:

Number_Of_Beans

XXjXugtXty

hhjhugthty


EDIT:

Hal di atas dapat diimplementasikan dengan cara yang lebih cocok, jika pertunjukan menjadi perhatian Anda, dengan tidak mengembalikan apa pun ( void) dan melakukan perubahan secara langsung pada string yang strdiberikan sebagai argumen, diteruskan dengan alamat alih-alih berdasarkan nilai . Ini akan menghindari salinan string asli yang tidak berguna dan mahal, sambil mengembalikan hasilnya. Teleponmu, lalu ...

Kode:

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

Semoga ini akan bermanfaat bagi beberapa orang lain ...

Gauthier Boaglio
sumber
4
Yang ini memiliki masalah kinerja dalam kasus-kasus di mana string sumber besar dan ada banyak kemunculan string yang akan diganti. string :: replace () akan dipanggil berkali-kali yang menyebabkan banyak salinan string. Lihat solusi saya yang mengatasi masalah itu.
minastaros
1
Nit picking ahead: by address => dengan referensi . Apakah itu alamat atau bukan adalah detail implementasi.
Max Truxa
1
Anda harus benar-benar memeriksa apakah fromstring kosong, jika tidak, loop tanpa akhir akan terjadi.
pemula
34

Bayangkan gumpalan biner besar di mana semua 0x00 byte akan diganti oleh "\ 1 \ x30" dan semua 0x01 byte oleh "\ 1 \ x31" karena protokol transport tidak mengizinkan \ 0-byte.

Dalam kasus di mana:

  • string pengganti dan yang diganti memiliki panjang yang berbeda,
  • ada banyak kemunculan string yang diganti di dalam string sumber dan
  • string sumber besar,

solusi yang disediakan tidak dapat diterapkan (karena mereka hanya mengganti karakter tunggal) atau memiliki masalah kinerja, karena mereka akan memanggil string :: ganti beberapa kali yang menghasilkan salinan ukuran gumpalan berulang-ulang. (Saya tidak tahu solusi peningkatannya, mungkin tidak apa-apa dari perspektif itu)

Yang ini berjalan di sepanjang semua kemunculan dalam string sumber dan membuat string baru sepotong demi sepotong sekali :

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);
}
minastaros
sumber
Sejauh ini, ini adalah solusi terbaik yang dibangun di atas STL saja. Jika Anda ingin menggunakan fungsi khusus agar mudah digunakan di mana saja, buat yang ini.
Roger Sanders
21

Pencarian dan penggantian sederhana untuk satu karakter akan menghasilkan sesuatu seperti:

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

Untuk melakukan ini pada seluruh string, hal yang mudah dilakukan adalah mengulang sampai Anda s.findmulai kembali npos. Saya kira Anda juga bisa menangkap range_erroruntuk keluar dari loop, tapi itu agak jelek.

TED
sumber
7
Meskipun ini mungkin solusi yang cocok ketika jumlah karakter yang akan diganti kecil dibandingkan dengan panjang string, itu tidak skala dengan baik. Karena proporsi karakter dalam string asli yang perlu diganti meningkat, metode ini akan mendekati O (N ^ 2) pada waktunya.
dan dan
7
Benar. Filosofi umum saya adalah melakukan hal yang mudah (untuk menulis dan membaca) sampai saat inefisiensi menyebabkan masalah nyata. Ada beberapa keadaan di mana Anda mungkin memiliki string humoungous di mana O (N ** 2) penting, tetapi 99% dari waktu string saya adalah 1K atau kurang.
TED
3
... itu dikatakan, aku lebih suka metode Kirill (dan sudah memilih itu).
TED
Apa yang terjadi jika "x" tidak ditemukan? Juga, mengapa Anda menggunakan kawat gigi ganda?
Prasath Govind
@PrasathGovind - Saya baru saja menunjukkan panggilan yang diperlukan (karenanya "sesuatu seperti"). Detail penting namun tidak jelas seperti penanganan kesalahan yang tepat dibiarkan sebagai latihan bagi pembaca. Adapun "kawat gigi ganda", saya tidak yakin apa itu, atau apa yang Anda bicarakan. Bagi saya "kurungan" adalah {karakternya. Saya tidak tahu apa itu "penjepit ganda". Mungkin Anda memiliki masalah font?
TED
6

Jika Anda ingin mengganti lebih dari satu karakter, dan hanya berurusan dengan std::string, maka cuplikan ini akan berfungsi, mengganti sNeedle di sHaystack dengan sReplace, dan sNeedle dan sReplace tidak perlu memiliki ukuran yang sama. Rutin ini menggunakan loop sementara untuk menggantikan semua kejadian, bukan hanya yang pertama ditemukan dari kiri ke kanan.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Volomike
sumber
Ini adalah O (n ^). Anda dapat melakukannya dalam waktu O (n).
Changming Sun
3
@ ChangmingSun solusi O (n) apa yang Anda maksud?
habakuk
2
Ini akan infinite loop jika kNeedle menjadi substring dari sReplace.
prideout
Ditambah lagi ada findpanggilan dua kali. Pertimbangkan menjadikan hasil itu sebagai variabel temp.
Luc Bloom
4

Seperti yang disarankan Kirill, baik menggunakan metode ganti atau beralih sepanjang string menggantikan masing-masing karakter secara mandiri.

Atau Anda dapat menggunakan findmetode ini atau find_first_oftergantung pada apa yang perlu Anda lakukan. Tak satu pun dari solusi ini akan melakukan pekerjaan sekaligus, tetapi dengan beberapa baris kode tambahan Anda harus membuatnya bekerja untuk Anda. :-)

Konrad
sumber
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;
}
Lloydie
sumber
Jika wordpanjang, mungkin ada banyak overhead saat memanggil fungsi. Anda dapat mengoptimalkan ini dengan melewati word, targetdan replacementsebagai const-referensi.
TrebledJ
2

Bagaimana dengan Abseil StrReplaceAll ? Dari file header:

// 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;"}});
hotblack944
sumber
1

Sekolah Lama :-)

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;

Hasil:

H: \ recursos \ audio \ youtube \ libre \ falta \

Iván Rodríguez
sumber
0

Ini bekerja! Saya menggunakan sesuatu yang mirip dengan ini untuk aplikasi toko buku, di mana inventaris disimpan dalam CSV (seperti file .dat). Tetapi dalam kasus char tunggal, artinya replacer hanya char tunggal, misalnya '|', harus dalam tanda kutip ganda "|" agar tidak membuang char const konversi yang tidak valid.

#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

Biasanya saya menggunakan CentOS saat ini, jadi versi kompiler saya ada di bawah ini. Versi C ++ (g ++), standar 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.
oOpSgEo
sumber
0

Jika Anda ingin menggunakan std::strings, Anda dapat menggunakan strsubfungsi aplikasi sampel ini apa adanya, atau memutakhirkannya jika Anda ingin menggunakan jenis atau set parameter yang berbeda untuk mencapai kira-kira tujuan yang sama. Pada dasarnya, ia menggunakan properti dan fungsi std::stringuntuk dengan cepat menghapus set karakter yang cocok, dan menyisipkan karakter yang diinginkan langsung di dalam std::string. Setiap kali ia melakukan operasi penggantian ini, offset akan diperbarui jika masih dapat menemukan karakter yang cocok untuk diganti, dan jika tidak bisa karena tidak ada lagi yang harus diganti, ia mengembalikan string dalam keadaannya dari pembaruan terakhir.

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

Jika Anda tidak ingin mengandalkan menggunakan std::strings sebagai parameter sehingga Anda dapat meneruskan string C-style, Anda dapat melihat sampel yang diperbarui di bawah ini:

#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;
}
kayleeFrye_onDeck
sumber
0

Untuk situasi sederhana ini berfungsi dengan baik tanpa menggunakan perpustakaan lain lalu std :: string (yang sudah digunakan).

Ganti semua kemunculan karakter a dengan karakter b di some_string :

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

Jika string besar atau beberapa panggilan untuk diganti adalah masalah, Anda dapat menerapkan teknik yang disebutkan dalam jawaban ini: https://stackoverflow.com/a/29752943/3622300

Guney Ozsan
sumber
0

inilah solusi yang saya putar, dalam semangat DRI maksimal. ia akan mencari sNeedle di sHaystack dan menggantinya dengan sReplace, nTimes jika bukan 0, kalau tidak semua kejadian sNeedle. itu tidak akan mencari lagi dalam teks yang diganti.

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