Standart bir dizede nasıl Arar / Bulur ve Değiştiririm?


Yanıtlar:


75

Neden kendi değişiminizi uygulamıyorsunuz?

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

3
"Değiştirmek" için yapılan tüm çağrılarla burada hafızayı biraz karıştırıyorsunuz: "o" u "ooooooo ... o" dan kaldırırsanız karmaşıklık n² olur. Sanırım biri daha iyisini yapabilir, ancak bu çözümün anlaşılması kolay olma değeri vardır.
Zonko

1
Bu neden karmaşık bir for döngüsü değil de gerçek bir for döngüsü değil?
Shirik

'En az sürpriz' ilkesini uygulamaya alışkınım. Döngüler için, çoğu zaman basit indeks artış kullanımı içindir. Burada bana göre bir while döngüsü daha net.
yves Baumes

1
@aldo Genel bir kural olarak, karmaşıklıktan kaçınmak ve örneğin diğer yanıtlarda belirtildiği gibi normal ifadeyi kullanmak daha iyidir. Ancak ihtiyacınıza bağlı olarak proje bağımlılıklarınızı kontrol etmek isteyebilirsiniz. Artık tam olarak ihtiyacınız olan şeyi yapan küçük bir kod parçacığı bazen daha iyidir.
yves Baumes

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

Replace_all ile ilgili resmi belgeler burada .


1
Kalıp ve değiştirme için açıkça std :: string'ler oluşturmanız gerekmediğini unutmayın: boost :: replace_all (target, "foo", "bar");
Alexis Wilke

4
+1, bir uyarı ile: replace_allSun Studio <12.3 sürümleri için> 1.43 yükseltme sürümleri için segfault olacak
Brian Vandenberg

3
boostgömülü aygıtlarda derleme süresini önemli ölçüde artırır. ARMv7 dört çekirdekli bile. 2 dakikada, 2 saniyede hızlandırma olmadan 100 satır kod derlenir.
Piotr Kula

4
@ppumkin: bu, derleyicinizin (veya kurulum kurulumunuz veya her neyse) berbat olduğu anlamına gelir, onunla hiçbir ilgisi olmayan hedef mimariyi değil.
Daniel Kamil Kozar

Derleyiciniz önceden derlenmiş üstbilgiyi destekliyorsa, yükseltme kullanılırken kullanılması şiddetle tavsiye edilir. Gerçekten zaman kazandırır.
Alexey Omelchenko

33

C ++ 11'de, bunu aşağıdakilere çağrı yaparak tek satırlık olarak yapabilirsiniz 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;

çıktı:

Removeallspaces

Teşekkürler, kullanımı ve hatırlaması çok kolay!
Julian Declercq

fromBunun normal bir ifade de olabileceğine dikkat edin - böylece gerekirse daha karmaşık eşleştirme ölçütleri kullanabilirsiniz. Ne, görmüyorum bunun nasıl olduğunu olmaksızın yerine yalnızca doğrudan bir yorumunu kullanırken - Normal ifade ayrıştırma çeşit uygulayarak fromkarakterler.
Brent Bradburn

Bu, güncel bir derleyici gerektirebilir. Gcc 5.0 ile çalıştı ama gcc 4.8.4 ile bazı sorunlar yaşadım.
Brent Bradburn

@nobar, evet, doğru hatırlıyorsam 4.8.x'teki normal ifade desteği tamamlanmadı. Ayrıca daha karmaşık aramalarınız olabilir, ancak zaman açısından cezalandırılırsınız ... Diğer daha basit arama ve değiştirme işlevlerinden daha yavaş olacaktır.
Alexis Wilke

2
Lütfen bunun yalnızca çok temel alfasayısal karakterler için işe yarayacağını ve dize türüne bağlı olarak çok fazla ön işlem yapmadan başka hiçbir şey yapmayacağını unutmayın. Henüz genel amaçlı regex tabanlı bir dize değişimi bulamadım.
Piyush Soni

17

Neden değiştirilmiş bir dize döndürmüyorsunuz?

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

Performansa ihtiyacınız varsa, işte giriş dizesini değiştiren optimize edilmiş bir işlev, dizenin bir kopyasını oluşturmaz:

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

Testler:

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;

Çıktı:

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

6

Şablon haline getirilmiş satır içi yerinde bul ve değiştir:

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

Değiştirilen öğelerin sayısını döndürür (bunu art arda çalıştırmak istiyorsanız kullanmak için vb.). Kullanmak için:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
Bu örneği GCC altında denedim ama derlenmedi - T :: size_t kullanımını beğenmedi. T :: size_t yerine typeename T :: size_type koymak sorunu çözer.
Andrew Wyatt

3

En kolay yol (yazdıklarınıza yakın bir şey sunmak) Boost.Regex'i , özellikle regex_replace'i kullanmaktır .

std :: string yerleşik find () ve replace () yöntemlerine sahiptir, ancak indisler ve dizi uzunluklarıyla uğraşmayı gerektirdiklerinden çalışmak daha zahmetlidir.


3
Ayrıca replace_all dahil olmak üzere boost string algoritmaları da vardır (regex, bu kadar basit ikame için biraz ağır olabilir).
UncleBens

3

Bunun işe yarayacağına inanıyorum. Parametre olarak const char * 'ler alır.

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

Olduğu göz önüne alındığında size_typebir dize için unsigned, sizin >=döngü durumunda çek hep olacaktır true. std::string::nposOrada kullanmalısın .
Pavel Minaev

size_type işaretsiz değil. Pek çok platformda imzasız ama hepsinde değil.
Alan

12
Neden dünyada bu std :: string'in bir parçası değil? Programlama dünyasında 'bul ve değiştir' işlemi sunmayan başka ciddi String sınıfı var mı? Elbette iki yineleyiciye sahip olmaktan ve aralarındaki metni değiştirmek istemekten daha yaygındır ?? Bazen std :: string, ayarlanabilir spektrumlu bir ön cama sahip bir araba gibi hissediyor, ancak sürücünün camından aşağı yuvarlanma yolu yok.
Spike0xff

@ Spike0xff boost hasroll_down_window
ta.speot.is

1
@gustafr: Benim hatam. Eski derleyicilerin size_t'yi yanlış tanımladığı sistemler üzerinde çalıştım.
Alan

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

OldStr'nin boş olması için kontrol önemlidir. Herhangi bir nedenle bu parametre boşsa, sonsuz bir döngüde sıkışıp kalacaksınız.

Ama evet, mümkünse denenmiş ve test edilmiş C ++ 11 veya Boost çözümünü kullanın.


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

1
Sadece son maçtan yeni maçlar aramamız gerekiyor, bu yüzden algoritma pozisyondaki son maçı dikkatlice izliyor pos2 her zaman sonraki eşleşmeyi saklar, bu nedenle dizgeyi pos ve pos2 arasında sonuçla birleştiririz, sonra pos ve pos2'yi ilerletiriz. Başka bir eşleşme bulunamazsa, dizenin kalanını sonuca birleştiririz.
Björn Ganster
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.