Dize içindeki bir karakterin tüm tekrarları nasıl değiştirilir?


Yanıtlar:


742

std::stringböyle bir işlev içermez, ancak üstbilgiden bağımsız replaceişlevi kullanabilirsiniz algorithm.

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

6
std::stringolan bir kap , özellikle karakter dizisi ile çalışmak üzere dizayn edilmiştir. bağlantı
Kirill V. Lyadvinsky

164
Ne yazık ki, bu sadece bir karakter başka bir karakter ile değiştirilir. Bir karakteri daha fazla karakterle (yani bir dizeyle) değiştiremez. Daha fazla karakterle arama değiştirme yapmanın bir yolu var mı?
SasQ

6
@Kirill V. Lyadvinsky Ne Sadece bir olayı kaldırmak istersem.
SIFE

4
@ KirillV.Lyadvinsky: Tüm x'leri y'lerle değiştirmek için bu yöntemi kullandığımda, sonuç orijinal dize ne olursa olsun uzun bir dizedir. Sorunun ne olacağını merak ediyorum. (kod yazdığınız kodun aynısıdır)
Transcendent

6
@Transcendent: Bunun std::string::replace()yerine tam olarak ne oluyor std::replace()! 'x' ( char), size_t[değer 120] 'ye dolaysız olarak dökülür , bu nedenle dizenin tamamı veya bir kısmı 120 kopya' y 'ile doldurulur.
IBue

127

Ben de destek çözümü de atmak düşündüm :

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

Sonra -Isisteminizde Boost kütüphanelerini bulmak için derleyiciniz için birkaç bayrak eksik . Belki de önce yüklemeniz gerekir.
Martin Ueding

O ;-) tüm kullanarak artırmak kitaplığında lib.No std çıkar çünkü yukarıda daha etkilidir
hfrmobile

122

Soru characterdeğiştirme üzerine odaklanmıştır , ancak bu sayfayı çok yararlı bulduğumdan (özellikle Konrad'ın sözleri), bu daha genel uygulamayı paylaşmak istiyorum, bu da başa çıkmaya izin veriyor substrings:

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

Kullanımı:

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;

Çıktılar:

Number_Of_Beans

XXjXugtXty

hhjhugthty


DÜZENLE:

Performanslar endişe duymanız durumunda, hiçbir şey ( void) döndürmeyerek ve değere göre adres yerine striletilen bağımsız değişken olarak verilen dize üzerinde doğrudan değişiklikler gerçekleştirerek yukarıdakiler daha uygun bir şekilde uygulanabilir . Bu, sonucu döndürürken orijinal dizenin işe yaramaz ve maliyetli bir kopyasını önler. Çağrınız o zaman ...

Kod:

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

Bu diğerleri için yararlı olacağını umuyoruz ...


4
Bu, kaynak dizenin büyük olduğu ve dizenin değiştirilecek birçok oluşumunun olduğu durumlarda bir performans sorununa sahiptir. string :: replace () birçok kez çağrılır ve bu da çok sayıda dize kopyasına neden olur. Bu sorunu çözen çözümüme bakın.
minastaros

1
Nit toplama öncesinde: adrese göre => referans olarak . Adres olup olmadığı uygulama detayıdır.
Max Truxa

1
Aslında fromdizenin boş olup olmadığını kontrol etmelisiniz , aksi takdirde sonsuz bir döngü oluşur.
acemi

34

Taşıma protokolünün \ 0-bayta izin vermediği için, tüm 0x00 baytlarının "\ 1 \ x30" ve tüm 0x01 baytlarının "\ 1 \ x31" ile değiştirileceği büyük bir ikili blob düşünün.

Aşağıdaki durumlarda:

  • değiştirilecek ve değiştirilecek dizenin farklı uzunlukları vardır,
  • kaynak dizgide değiştirilecek dizenin birçok örneği vardır ve
  • kaynak dize büyük,

sağlanan çözümler uygulanamaz (çünkü yalnızca tek karakterleri değiştirirler) veya bir performans problemi vardır, çünkü dize :: replace'i birkaç kez çağırırlar, bu da blob boyutunun tekrar tekrar kopyasını oluşturur. (Güçlendirme çözümünü bilmiyorum, belki bu açıdan sorun yok)

Bu, kaynak dizgideki tüm oluşumlar boyunca yürür ve yeni dizeyi bir kez parça parça oluşturur :

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

Bu, burada sadece STL üzerine inşa edilmiş en iyi çözümdür. Her yerde kolay kullanım için özel bir işleve bırakacaksanız, bunu yapın.
Roger Sanders

21

Tek bir karakter için basit bir bulma ve değiştirme şöyle bir şey olur:

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

Tüm dize için bunu yapmak için, yapılacak en kolay şey s.findgeri dönmeye başlayana kadar döngü olacaktır npos. Sanırım range_errordöngüden çıkmak için de yakalayabilirsiniz , ama bu biraz çirkin.


7
Değiştirilecek karakter sayısı dizenin uzunluğuna göre az olduğunda bu muhtemelen uygun bir çözüm olsa da, iyi ölçeklenmez. Orijinal dizedeki değiştirilmesi gereken karakterlerin oranı arttıkça, bu yöntem zamanla O (N ^ 2) 'ye yaklaşacaktır.
ve

7
Doğru. Genel felsefem, verimsizlikler gerçek sorunlara neden olana kadar kolay (yazma ve okuma) şeyi yapmaktır. O (N ** 2) önemli olan humoungo string'lerin olabileceği bazı durumlar vardır, ancak stringlerimin 1K veya daha az olduğu zamanların% 99'u.
TED

3
... Kirill'in yöntemini daha çok seviyorum (ve zaten oy vermiştim).
TED

"X" bulunmazsa ne olur? Ayrıca, neden çift ayraç kullanıyorsunuz?
Prasath Govind

@PrasathGovind - Sadece gerekli çağrıları gösteriyordum (dolayısıyla "gibi bir şey"). Hatalı kullanım gibi önemli ancak belirsiz detaylar okuyucu için bir alıştırma olarak bırakılmıştır. "Çift parantez" gelince, bunların ne olduğundan veya ne hakkında konuştuğunuzdan emin değilim. Benim için bir "küme ayracı" {karakter. "Çifte parantez" in ne olduđunu bilmiyorum. Belki bir tür yazı sorununuz var mı?
TED

6

Tek bir karakterden daha fazlasını değiştirmek istiyorsanız ve sadece bununla uğraşıyorsanız std::string, bu snippet çalışır, sHaystack'taki sNeedle'ı sReplace ile değiştirir ve sNeedle ve sReplace'in aynı boyutta olması gerekmez. Bu yordam, soldan sağa yalnızca birincisi yerine tüm olayları değiştirmek için while döngüsünü kullanır.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}

Bu O (n ^). O (n) zamanda yapabilirsin.
Changming Sun

3
Hangi O (n) çözümü demek istediniz?
habakuk

2
Eğer kNeedle sReplace'in bir alt dizesi olursa, bu sonsuz döngü olacaktır.
prideout

Ayrıca findiki kez bir çağrı var . Bu sonucu temp değişkeni yapmayı düşünün.
Luc Bloom

4

Kirill'in önerdiği gibi, replace yöntemini kullanın veya her bir karakteri bağımsız olarak değiştiren dize boyunca yineleyin.

Alternatif olarak, findyöntemi veya find_first_ofne yapmanız gerektiğine bağlı olarak kullanabilirsiniz . Bu çözümlerin hiçbiri işi bir seferde yapmayacak, ancak birkaç ekstra kod satırıyla bunları sizin için çalıştırabilmelisiniz. :-)


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

Eğer worduzun işlevi arama sırasında, yükü bir sürü olabilir. Sen geçirerek bu optimize edebilirsiniz word, targetve replacementconst-referans olarak.
TrebledJ

2

Abseil StrReplaceAll ne olacak ? Başlık dosyasından:

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

1

Eski okul :-)

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;

Sonuç:

H: \ Recursos \ ses \ youtube \ libre \ falta \


0

Bu çalışıyor! Envanterin bir CSV'de (bir .dat dosyası gibi) depolandığı bir kitapçı uygulaması için buna benzer bir şey kullandım. Ancak tek bir karakter söz konusu olduğunda, yani yer değiştirici yalnızca tek bir karakterdir, örneğin '|', çift tırnak işareti "|" olmalıdır. geçersiz bir dönüşüm const char atmamak için.

#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

Alışılmadık şu anda CentOS kullanıyorum, bu yüzden derleyici sürümüm aşağıda. C ++ sürümü (g ++), C ++ 98 varsayılanı:

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.

0

S'yi kullanmak std::stringistiyorsanız, bu örnek uygulamanın strsubişlevini olduğu gibi kullanabilir veya kabaca aynı hedefe ulaşmak için farklı bir tür veya parametre kümesi almasını istiyorsanız güncelleyebilirsiniz. Temel olarak, std::stringeşleşen karakter kümesini hızlı bir şekilde silmek ve istenen karakterleri doğrudan std::string. Bu değiştirme işlemini her gerçekleştirdiğinde, dengeleme yine de değiştirilecek karakterleri bulabilirse güncellenir ve değiştirilecek başka bir şey olmadığından, dizeyi son güncellemeden itibaren durumuna döndürür.

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

std::stringParametreleriniz olarak s kullanmaya güvenmek istemiyorsanız, bunun yerine C stili dizeleri aktarabilirsiniz, aşağıdaki güncellenmiş örneği görebilirsiniz:

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

0

Basit durumlar için bu başka bir kütüphane kullanmadan oldukça iyi çalışır sonra std :: string (zaten kullanımda).

Karakter tüm tekrarlarını değiştirin a karakteri ile b içinde some_string :

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

Dize büyükse veya değiştirmek için birden fazla çağrı varsa, bu yanıtta belirtilen tekniği uygulayabilirsiniz: https://stackoverflow.com/a/29752943/3622300


0

İşte en büyük DRI ruhuyla yuvarladığım bir çözüm. sHaystack'te sNeedle öğesini arar ve sReplace ile değiştirir, n 0 değilse, nTimes, aksi takdirde tüm sNeedle oluşumları. değiştirilen metinde tekrar arama yapmaz.

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