Bir dizenin bir kısmını başka bir dizeyle değiştir


186

C ++ 'da bir dizenin bir kısmını başka bir dizeyle değiştirmek mümkün mü?

Temel olarak, bunu yapmak istiyorum:

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

Ancak Standard C ++ kütüphanelerini kullanmak istiyorum.


1
olası yinelenen Ne C dize yerine işlevdir? - Üzgünüz, C ++ değil C; Keşke oy açabilsem.
poligeno

1
@poly Bunun C ++ için de sorulmuş olması gerektiğini düşünürdüm, ama bulamıyorum
Michael Mrozek

1
Soruda bir std etiketi var, ancak belki de geniş bir değiştirme algoritması seçeneği (inplace / copy, büyük / küçük harfe duyarlı / büyük / küçük harfe duyarlı olmayan, ilk / son / all / n-th'yi de içeren boost dize algoritmalarıyla ilgilenebilirsiniz. ).
amca

@Michael Mrozek stackoverflow.com/questions/3418231/… adresinde bir tane var, ancak daha yeni ve replaceAll yönteminiz daha sağlam.
dave-holm

Yanıtlar:


288

Bir dize ( find) içinde bir alt dize bulma işlevi ve bir dizedeki belirli bir aralığı başka bir dize ( replace) ile değiştirme işlevi vardır , böylece istediğiniz efekti elde etmek için bunları birleştirebilirsiniz:

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

Bir yoruma yanıt olarak, replaceAllmuhtemelen şöyle bir şey olacağını düşünüyorum :

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

2
Orijinal dize "$ adı" bir örneği daha varsa ve hepsini değiştirmek istedim nasıl düzeltebilirim.
Tom Leese

1
Neden değildir fromve tobaşına geçti constreferans? Orada yoksa işleviniz ne yapar from? -1Bunun için benden.
sbi

10
@sbi Düzeltildi, ancak saldırılar yerine öneriler olarak ifade edebilseydiniz - bu sadece bana olmadı, nadiren kullanmayı düşünüyorum constve böyle bir yardımcı yöntem yazsaydım, yalnızca değiştirme geçerli
Michael Mrozek

10
@Michael: Güzel, aşağı oyumu bir yukarı oy haline getirdim. Kapatmak const, C ++ 'ın en iyi araçlarından birini göz ardı etmektir. Referans başına constgeçiş, işlev parametreleri için varsayılan mod olmalıdır. (FTR, olmadan, constişlevinize dizgi değişmezlerini bile geçiremezsiniz, çünkü geçici olmayanları constreferans olmayanlara bağlayamazsınız . Bu nedenle işlev, yazıldığını bile yapmaz.)
sbi

19
2018'de hala tek çözüm bu mu? Öyleyse ve herhangi bir C ++ komitesi bunu okuyorsa, sıralayın. Utanç verici. lütfen bölün (string, string) ve değiştirin (string, string) lütfen!
user997112

96

C ++ 11 ile şöyle kullanabilirsiniz std::regex:

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

Bir kaçış karakterinden kaçmak için çift ters eğik çizgi gereklidir.


Eminim std::regex_replaceQt dizesini kabul etmiyor.
BartoszKP

1
Haklısın. Olduğu gibi QString, QRexExp'i kabul eden ve Qt'un kendi malzemelerini kullanmasına izin veren bir değiştirme yöntemi sağlar. Ama şimdiki cevap değiştirerek düzeltilebilir düşünüyorum stringile string.toStdString().
Tom

1
Ya da sadece değiştirerek Stringiçin std::stringsoru Qt ile ilgili değil, çünkü. Lütfen bunu yapmayı düşünün - Daha sonra cevabınızı memnuniyetle onaylayacağım.
BartoszKP

5
Ham dize R"(\$name)"yerine yazmaya izin verir "\\$name".
Jarod42

2
Std :: regex'in yapım süresini düşünmeden bulmak / değiştirmekten ne kadar yavaş olacak?
jw_

18

std::stringbir replaceyöntemi var, aradığınız şey bu mu?

Deneyebilirsiniz:

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

Kendimi denemedim, sadece find()ve üzerindeki belgeleri okuyun replace().


2
Ne görebiliyorum std :: string replace yöntemi istediğim gibi iki dize almaz.
Tom Leese

2
Bu benim için işe yaramıyor. sizeof yerine string ("Somename") verilmelidir. size () - 1
TimZaman

@TimZaman: Bu beni şaşırtıyor, belgeler açıkça C tarzı bir dizeden başlatabileceğinizi gösteriyor.
SC Madsen

5
2. argüman "$ name" uzunluğu olmalıdır ("Somename" yerine), değil mi?
Daniel Kiss

10

Yeni dizenin döndürülmesi için şunu kullanın:

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, burada giriş dizesini değiştiren optimize edilmiş bir işlev vardır, 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 modified: " 
          << 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

ReplaceStringInPlace () içindeki topic.replace çağrınız, dizeyi gerçekten yerinde değiştiriyor mu?
Damian

Kısaca kaynağa baktım ve eski dizenin önünü yerine taşımak için hareket semantiğini kullanıyor gibi görünüyor, bu yüzden kopyalanmıyor, ancak eklenen yeni parça eski dizeye ve eski dizenin kuyruğuna kopyalanıyor eski dizgenin arabelleğe alınan boyutuna kopyalanır. Dize, altta yatan arabellek yeniden tahsis edilen kadar genişler mümkündür, ancak örneğinde olduğu gibi 1 ila 1 yerine, "yerinde" veya herhangi bir kopya olmadan gerçekleşir, ancak dize genişletirseniz, eski dizgenin yalnızca ilk kısmı kopyalanmaz ve yalnızca o zaman kopyalanır.
Motes

6

Evet, bunu yapabilirsiniz, ancak ilk dizenin string'in find () üyesiyle konumunu bulmalı ve yerine replace () üyesiyle değiştirmelisiniz.

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

Standart Kütüphaneyi kullanmayı planlıyorsanız, tüm bunları çok iyi kapsayan C ++ Standart Kütüphanesi kitabının bir kopyasını gerçekten tutmalısınız .


1
size_tpe ve size_type değil
revo

1
Bu std :: string :: size_type, size_t veya süslenmemiş size_type değil.
jmucchiello

5

Ben genellikle bu kullanın:

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

Hiçbir şey std::string::find()bulana kadar aranan dizenin diğer oluşumlarını bulmak için tekrar tekrar çağırır std::string::find(). Eşleşmenin konumunustd::string::find() döndürdüğü için yineleyicileri geçersiz kılma sorunumuz yoktur.


4

Bu bir seçenek gibi geliyor

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

Bunu bir işlevde sarabilirsiniz, ancak bu tek satırlık çözüm kabul edilebilir geliyor. Sorun şu ki, bu sadece ilk oluşumu değiştirecek, üzerinde döngü yapmak isteyebilirsiniz, ancak aynı dizeye ( %s) aynı token ( ) ile birkaç değişken eklemenize izin verir


1
Tarzı gibi ama farklı dizeleri kafa karıştırıcı bulundu ^^str.replace(str.find("%s"), string("%s").size(), "Something");
Paul Würtz

3

Tüm dizeler std :: string ise, sizeof()C ++ dizeleri için değil C dizeleri için kullanıldığından, karakterlerin kesilmesiyle garip sorunlar bulacaksınız . Düzeltme, .size()sınıf yöntemini kullanmaktır std::string.

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

Bu, sHaystack inline'ın yerini alıyor - buna bir = ataması yapmaya gerek yok.

Örnek kullanım:

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;

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

2

Hızlı bir şekilde yapmak istiyorsanız, iki tarama yaklaşımı kullanabilirsiniz. Sahte kod:

  1. ilk ayrıştırma. kaç eşleşen karakter bulun.
  2. dizenin uzunluğunu genişlet.
  3. ikinci ayrıştırma. Değiştirdiğimiz bir eşleşme elde ettiğimizde dizenin sonundan başlayın, yoksa sadece ilk dizeden karakterleri kopyalarız.

Bunun yerinde bir algo için optimize edilip edilemeyeceğinden emin değilim.

Ve bir C ++ 11 kod örneği ama ben sadece bir karakter arar.

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

}

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

2
Lütfen bu kodu açıklayabilir misiniz (cevabınızda)? Bu şekilde daha fazla oy alabilirsiniz!
Şapkalı Adam

1

Benim dize sadece bir kez yeniden boyutlandırılması gerektiğini göz önünde bulundurarak kendi uygulama, sonra değiştirme olabilir.

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

Kullanım örneğin şöyle olabilir:

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

0

Ben sadece C ++ öğreniyorum, ama daha önce yayınlanan kod bazı düzenleme, muhtemelen böyle bir şey kullanmak istiyorum. Bu size 1 veya daha fazla örneği değiştirme esnekliği sağlar ve ayrıca başlangıç ​​noktasını belirlemenizi sağlar.

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

0

Bunu kullanmak daha da iyi olabilir

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