Bagaimana cara mencari / menemukan dan mengganti dalam string standar?

94

Apakah ada cara untuk mengganti semua kemunculan substring dengan string lain di std::string?

Misalnya:

void SomeFunction(std::string& str)
{
   str = str.replace("hello", "world"); //< I'm looking for something nice like this
}
Adam Tegen
sumber

Jawaban:

75

Mengapa tidak menerapkan penggantian Anda sendiri?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}
yves Baumes
sumber
3
Anda mengacaukan sedikit memori di sini dengan semua panggilan untuk "menggantikan": kompleksitas akan menjadi n² jika Anda menghapus "o" dari "ooooooo ... o". Saya rasa ada yang bisa melakukan lebih baik, tetapi solusi ini bermanfaat karena mudah dipahami.
Zonko
1
Mengapa ini bukan loop for aktual, bukan loop for yang dikaburkan?
Shirik
Saya terbiasa menerapkan prinsip 'paling tidak mengejutkan'. Untuk loop adalah untuk penggunaan peningkatan indeks sederhana, sebagian besar waktu. Di sini, menurut saya, loop sementara lebih jelas.
yves Baumes
1
@aldo Sebagai aturan umum, lebih baik menghindari kompleksitas dan, misalnya, gunakan regex seperti yang disebutkan dalam balasan lain. Tetapi tergantung pada kebutuhan Anda, Anda mungkin ingin mengontrol dependensi proyek Anda. Cuplikan kode kecil yang melakukan apa yang sebenarnya Anda butuhkan, tidak lebih, terkadang lebih baik.
yves Baumes
158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

Berikut adalah dokumentasi resmi di replace_all.

TheNamelessOne
sumber
1
Perhatikan bahwa Anda tidak harus secara eksplisit membuat std :: string untuk pola dan penggantian: boost :: replace_all (target, "foo", "bar");
Alexis Wilke
4
+1, dengan peringatan: replace_allakan segfault untuk versi peningkatan> 1.43 di Sun Studio untuk versi apa pun <12.3
Brian Vandenberg
3
boostmeningkatkan waktu kompilasi secara signifikan pada perangkat yang disematkan. Bahkan quad core ARMv7. 100 baris kode dikompilasi dalam 2 menit, tanpa peningkatan, 2 detik.
Piotr Kula
4
@ppumkin: itu berarti kompilator Anda (atau penyiapan build, atau apa pun) menyebalkan, bukan arsitektur target, yang tidak ada hubungannya dengan itu.
Daniel Kamil Kozar
Jika kompilator Anda mendukung header yang telah dikompilasi sebelumnya, sangat disarankan untuk menggunakannya saat menggunakan boost. Ini sangat menghemat waktu.
Alexey Omelchenko
33

Di C ++ 11, Anda dapat melakukan ini sebagai satu baris dengan panggilan ke regex_replace:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

keluaran:

Removeallspaces
Brent Bradburn
sumber
Terima kasih, sangat mudah digunakan dan diingat!
Julian Declercq
Perhatikan juga bahwa itu frombisa menjadi ekspresi reguler - sehingga Anda bisa menggunakan kriteria pencocokan yang lebih canggih jika perlu. Apa yang tidak saya lihat adalah bagaimana melakukan ini tanpa menerapkan beberapa bentuk penguraian ekspresi reguler - alih-alih hanya menggunakan interpretasi langsung dari fromkarakter.
Brent Bradburn
Ini mungkin membutuhkan kompiler terbaru. Ini berfungsi dengan gcc 5.0, tetapi saya mengalami beberapa masalah dengan gcc 4.8.4.
Brent Bradburn
@nobar, ya, jika saya ingat dengan benar dukungan regex di 4.8.x tidak lengkap. Anda juga dapat melakukan pencarian yang lebih canggih, tetapi Anda mendapatkan penalti berdasarkan waktu ... Ini akan menjadi lebih lambat daripada fungsi pencarian dan penggantian yang lebih lurus ke depan.
Alexis Wilke
2
Harap dicatat bahwa ini hanya akan berfungsi untuk karakter alfanumerik yang sangat dasar dan tidak ada yang lain tanpa melakukan banyak praproses tergantung pada jenis string. Saya belum menemukan penggantian string berbasis regex tujuan umum.
Piyush Soni
17

Mengapa tidak mengembalikan string yang dimodifikasi?

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 adalah fungsi yang dioptimalkan yang mengubah string input, itu tidak membuat salinan dari 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 changed: " 
          << 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
6

Temukan-dan-ganti sebaris saya di tempat:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

Ini mengembalikan hitungan jumlah item yang diganti (untuk digunakan jika Anda ingin menjalankan ini secara berturut-turut, dll). Untuk menggunakannya:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");
Marius
sumber
4
Saya mencoba sampel ini di bawah GCC tetapi tidak dapat dikompilasi - tidak suka penggunaan T :: size_t. Mengganti T :: size_t dengan nama jenis T :: size_type memperbaiki masalah.
Andrew Wyatt
3

Cara termudah (menawarkan sesuatu yang mendekati apa yang Anda tulis) adalah dengan menggunakan Boost.Regex , khususnya regex_replace .

std :: string telah membangun metode find () dan replace (), tetapi metode ini lebih rumit untuk digunakan karena mereka memerlukan penanganan indeks dan panjang string.

Alan
sumber
3
Ada juga algoritma boost string, termasuk replace_all (regex mungkin agak berat untuk substitusi sederhana seperti itu).
UncleBens
3

Saya yakin ini akan berhasil. Dibutuhkan const char * sebagai parameter.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}
Adam Tegen
sumber
Mengingat size_typeuntuk string adalah unsigned, >=pemeriksaan Anda dalam kondisi loop akan selalu true. Anda harus menggunakannya di std::string::npossana.
Pavel Minaev
size_type [jenis_ukuran] tidak tidak ditandai Itu tidak ditandatangani di banyak platform, tetapi tidak semua.
Alan
12
Mengapa di dunia ini bukan bagian dari std :: string? Apakah ada kelas String serius lainnya di dunia pemrograman yang tidak menawarkan operasi 'temukan dan ganti'? Tentunya itu lebih umum daripada memiliki dua iterator dan ingin mengganti teks di antara keduanya ?? Terkadang std :: string terasa seperti mobil dengan kaca depan spektrum yang dapat disetel tetapi tidak ada cara untuk menurunkan jendela pengemudi.
Spike0xff
@ Spike0xff boost memilikiroll_down_window
ta.speot.is
1
@ Gustafr: Kesalahan saya. Saya telah mengerjakan sistem di mana kompiler lama mendefinisikan size_t secara tidak benar.
Alan
1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}
Björn Ganster
sumber
1
Kita hanya perlu mencari kecocokan baru dari kecocokan terakhir, itu sebabnya algoritma dengan hati-hati melacak kecocokan terakhir di pos. pos2 selalu menyimpan pertandingan berikutnya, jadi kami menggabungkan string antara pos dan pos2 ke hasil, lalu maju pos dan pos2. Jika tidak ada kecocokan lain yang dapat ditemukan, kami menggabungkan sisa string menjadi hasil.
Björn Ganster
1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

Pemeriksaan untuk oldStr kosong itu penting. Jika karena alasan apa pun parameter itu kosong, Anda akan terjebak dalam loop tak terbatas.

Tapi ya gunakan solusi C ++ 11 atau Boost yang telah dicoba dan diuji jika Anda bisa.

ericcurtin.dll
sumber