2012-04-05 4 views
3


Je souhaite remplacer un caractère de la chaîne par une chaîne. Puis-je le faire sur place? Comme la nouvelle chaîne a une longueur supérieure à la chaîne d'origine.Question est-ce que je peux faire avec l'utilisation de tampon supplémentaire? par exempleRemplacer char dans une chaîne avec une chaîne de caractères

void replaceChar(std::string &input, std::string replacementString, char charToReplace) 
{ 
//some code here. No additional buffer 
} 

void main(){ 

    std::string input = "I am posting a comment on LinkedIn"; 
    std::string replacementString = "pppp"; 
    char charToReplace = 'o'; 
    replaceChar(input, replacementString, charToReplace); 
} 

Je veux que la stratégie (algorithme). il serait bon que l'algorithme soit conçu en gardant à l'esprit un langage qui n'augmentera pas ou diminuera dynamiquement la longueur de la chaîne une fois initié comme C++

+1

Où prévoyez-vous les personnages supplémentaires seront affectés? – AShelly

+0

Ya thats ce que réellement je demande cela parce que j'ai vu une implémentation de code dans Java qui a calculé la nouvelle longueur de la chaîne et commence à placer des caractères de la fin au début. C'est pourquoi je me demande si java autorise ce type de changement de longueur de chaîne au moment de l'exécution ... – Madu

+0

Il existe un moyen d'insérer une chaîne commençant une position particulière, en supprimant le caractère à cette position seulement mais cela augmentera la taille de tampon interne. – hmjd

Répondre

4

std::string a un membre replace, mais il fonctionne en termes de positions numériques, plutôt que le contenu précédent de la chaîne. En tant que tel, vous devez normalement combiner avec l'élément find dans une boucle, quelque chose comme ceci:

std::string old("o"); 

int pos; 

while ((pos = x.find(old)) != std::string::npos) 
    x.replace(pos, old.length(), "pppp"); 

Personnellement, je rarement me préoccupé par la fréquence à laquelle la chaîne se redimensionnée, mais si elle est une préoccupation majeure , vous pouvez utiliser std::count pour trouver le nombre d'occurrences de la chaîne old, multiplier par la différence de taille entre l'ancienne et la nouvelle chaîne, et utiliser std::string::reserve() pour réserver suffisamment d'espace. Notez, cependant, que reserve a été ajouté dans C++ 11 - les anciennes implémentations ne l'auront pas. Edit: bien que cela ne soit pas un problème avec les chaînes que vous avez utilisées, comme l'a souligné @ipc, cela ne fonctionne pas correctement si la chaîne de remplacement contient une instance de la valeur à remplacer. Si vous avez besoin pour faire face à cela, vous devrez fournir le décalage dans la chaîne à laquelle commencer chaque recherche:

int pos = 0; 

while ((pos = x.find(old, pos)) != std::string::npos) { 
    x.replace(pos, old.length(), rep); 
    pos += rep.length(); 
} 

Ou, vous pouvez préférer une boucle for dans ce cas:

std::string old("o"); 
    std::string rep("pop"); 

for (int pos=0; 
    (pos = x.find(old, pos)) != std::string::npos; 
    pos+=rep.length()) 
{ 
    x.replace(pos, old.length(), rep); 
} 
+0

Si la chaîne de remplacement contient un «o», cela entraînera une boucle infinie. – ipc

+0

@ipc: bon point. J'ai ajouté un commentaire/code pour traiter de cela. –

+0

C'est un super cas de test ... – Madu

1

Je pense que vous avez mal compris C++ std :: string. Il peut réellement changer la longueur de la chaîne dynamiquement. En interne fait des allocations de tas, et augmentera le tampon si nécessaire.

0

Voici un code qui minimise le nombre d'affectations et d'allocations. Il est basé sur la réponse suivante à une question similaire: https://stackoverflow.com/a/32322122/3903076

Les cas où la chaîne de remplacement a la longueur 0 ou 1 sont traités séparément. Sinon, la chaîne doit grandir.

S'il n'y a pas assez de capacité, un tampon externe sera nécessaire de toute façon, donc nous ne faisons que copier-remplacer et échanger.

Le cas intéressant est quand la chaîne a déjà assez de capacité, ainsi nous pouvons réellement faire un remplacement in-situ non-trivial. Nous faisons cela avec une copie inverse-remplacer, en arrêtant quand nous n'avons pas besoin de remplacer quoi que ce soit d'autre.

Ceci peut être vu dans la dernière ligne de la fonction.

void replaceChar(std::string& input, const std::string& replacementString, char charToReplace) 
{ 
    if (replacementString.empty()) { 
    input.erase(std::remove(input.begin(), input.end(), charToReplace), input.end()); 
    return; 
    } 
    if (replacementString.size() == 1) { 
    std::replace(input.begin(), input.end(), charToReplace, replacementString.front()); 
    return; 
    } 

    const auto first_instance = std::find(input.begin(), input.end(), charToReplace); 
    auto count = std::count(first_instance, input.end(), charToReplace); 
    const auto extra_size = count * (replacementString.size() - 1); 
    const auto new_size = input.size() + extra_size; 

    if (input.capacity() < new_size) { 
    std::string aux; 
    aux.reserve(new_size); 
    replace_with_range_copy(input.cbegin(), input.cend(), std::back_inserter(aux), charToReplace, replacementString.cbegin(), replacementString.cend()); 
    input.swap(aux); 
    return; 
    } 

    input.resize(new_size); 

    const auto rlast = std::make_reverse_iterator(first_instance); 
    const auto rfirst = input.rbegin(); 
    const auto old_rfirst = rfirst + extra_size; 

    replace_with_range_copy(old_rfirst, rlast, rfirst, charToReplace, replacementString.crbegin(), replacementString.crend()); 
} 

Voici une implémentation de l'algorithme replace_with_range_copy:

template <typename InputIt1, typename OutputIt, typename T, typename InputIt2> 
OutputIt replace_with_range_copy(InputIt1 first, InputIt1 last, OutputIt d_first, const T& old_value, InputIt2 new_first, InputIt2 new_last) 
{ 
    InputIt1 next; 
    while (true) { 
    if (first == last) return d_first; 
    next = std::find(first, last, old_value); 
    d_first = std::copy(first, next, d_first); 
    if (next == last) return d_first; 
    d_first = std::copy(new_first, new_last, d_first); 
    first = std::next(next); 
    } 
} 
Questions connexes