Как заменить все вхождения символа в строку?

Ответы:

742

std::stringне содержит такой функции, но вы можете использовать автономную replaceфункцию из 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::stringпредставляет собой контейнер , специально предназначенный для работы с последовательностями символов. ссылка
Кирилл Васильевич Лядвинский
164
К сожалению, это позволяет заменить только один символ другим. Он не может заменить символ большим количеством символов (то есть строкой). Есть ли способ сделать поиск-заменить на несколько символов?
SasQ
6
@ Кирилл В. Лядвинский Что если я просто хочу удалить происшествие.
SIFE
4
@ KirillV.Lyadvinsky: Когда я использую этот метод, чтобы заменить все x на y, в результате получается длинная строка y, независимо от того, какая это исходная строка. Мне любопытно, как вы думаете, будет проблема. (код точно такой же, как вы написали)
Transcendent
6
@Transcendent: это именно то, что происходит std::string::replace()вместо std::replace()! 'x' ( char) косвенно приводится к size_t[значению 120], поэтому вся строка или ее часть будет заполнена 120 копиями y.
IBue
127

Я думал, что я также добавлю решение для повышения :

#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, "#", "@");
UncleZeiv
источник
Тогда вам не хватает нескольких -Iфлагов для вашего компилятора, чтобы он мог найти библиотеки Boost в вашей системе. Возможно, вам даже нужно сначала установить его.
Мартин Юдинг
Вышесказанное более эффективно, так как оно выходит со стандартной библиотекой lib.No не все используют библиотеку boost ;-)
hfrmobile
122

Вопрос сосредоточен на characterзамене, но, поскольку я нашел эту страницу очень полезной (особенно замечание Конрада ), я хотел бы поделиться этой более обобщенной реализацией, которая также позволяет иметь дело с 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;
}

Применение:

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;

Выходы:

Number_Of_Beans

XXjXugtXty

hhjhugthty


РЕДАКТИРОВАТЬ:

Вышеприведенное может быть реализовано более подходящим способом, в случае, если производительность вас интересует, ничего не возвращая ( void) и выполняя изменения непосредственно в строке, strзаданной в качестве аргумента, передаваемой по адресу, а не по значению . Это позволило бы избежать бесполезного и дорогостоящего копирования исходной строки при возврате результата. Твой звонок, тогда ...

Код:

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

Надеюсь, что это будет полезно для некоторых других ...

Готье Боальо
источник
4
В этом случае возникает проблема с производительностью в случаях, когда исходная строка велика и имеется много случаев замены строки. string :: replace () будет вызываться много раз, что вызывает много копий строк. Смотрите мое решение, которое решает эту проблему.
Минастарос
1
Ницца выбирая вперед: по адресу => по ссылке . Является ли это адрес или нет, это деталь реализации.
Макс Трукса
1
Вы должны на самом деле проверить, является ли fromстрока пустой, иначе произойдет бесконечный цикл.
новичок
34

Представьте большой двоичный двоичный объект, в котором все байты 0x00 должны быть заменены на «\ 1 \ x30», а все байты 0x01 - на «\ 1 \ x31», поскольку транспортный протокол не допускает байтов \ 0.

В случаях, когда:

  • замещающая и заменяемая строки имеют разную длину,
  • Есть много вхождений заменяемой строки в исходной строке и
  • исходная строка большая,

предоставленные решения не могут быть применены (потому что они заменяют только отдельные символы) или имеют проблемы с производительностью, потому что они будут вызывать string :: replace несколько раз, что будет генерировать копии размера большого двоичного объекта снова и снова. (Я не знаю, решение для повышения, может быть, это нормально с этой точки зрения)

Этот шаг проходит по всем вхождениям в исходной строке и строит новую строку по частям один раз :

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);
}
minastaros
источник
На сегодняшний день это лучшее решение, которое основано только на STL. Если вы собираетесь добавить пользовательскую функцию для удобного использования в любом месте, сделайте это.
Роджер Сандерс
21

Простой поиск и замена для одного символа будет выглядеть примерно так:

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

Чтобы сделать это для всей строки, проще всего сделать цикл до тех пор, пока не s.findначнется возврат npos. Я полагаю, вы также можете поймать, range_errorчтобы выйти из цикла, но это довольно уродливо.

ТЕД
источник
7
Хотя это, вероятно, подходящее решение, когда количество заменяемых символов мало по сравнению с длиной строки, оно плохо масштабируется. При увеличении доли символов в исходной строке, которые необходимо заменить, этот метод будет приближаться к O (N ^ 2) во времени.
andand
7
Правда. Моя общая философия - делать простые вещи (писать и читать) до тех пор, пока неэффективность не вызовет реальных проблем. Есть некоторые обстоятельства, когда у вас могут быть огромные строки, где O (N ** 2) имеет значение, но 99% времени мои строки составляют 1 КБ или меньше.
TED
3
... это, как говорится, мне больше нравится метод Кирилла (и уже проголосовал за него).
TED
Что произойдет, если «х» не найден? Кроме того, почему вы используете двойные скобки?
Прасат Говинд
@PrasathGovind - я просто показывал требуемые вызовы (отсюда "что-то вроде"). Важные, но неясные детали, такие как правильная обработка ошибок, были оставлены читателю в качестве упражнения. Что касается "двойных скобок", я не уверен, что это такое или о чем вы говорите. Для меня «фигурная скобка» - это {персонаж. Я не знаю, что такое "двойная скобка". Возможно, у вас есть какая-то проблема со шрифтом?
TED
6

Если вы хотите заменить более одного символа и имеете дело только с этим std::string, этот фрагмент будет работать, заменив sNeedle в sHaystack на sReplace, а sNeedle и sReplace не обязательно должны быть одинакового размера. Эта процедура использует цикл while для замены всех вхождений, а не только первый найденный слева направо.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Volomike
источник
Это O (n ^). Вы можете сделать это в O (N) время.
Чанминг Сан
3
@ChangmingSun какое O (n) решение вы имеете в виду?
Хабакук
2
Это будет бесконечный цикл, если kNeedle окажется подстрокой sReplace.
Прайдаут
Плюс есть findзвонок дважды. Попробуйте сделать этот результат временной переменной.
Люк Блум
4

Как предложил Кирилл, либо используйте метод replace, либо итерируйте по строке, заменяя каждый символ независимо.

В качестве альтернативы вы можете использовать findметод или в find_first_ofзависимости от того, что вам нужно сделать. Ни одно из этих решений не выполнит работу за один раз, но с несколькими дополнительными строками кода вы должны заставить их работать на вас. :-)

Konrad
источник
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;
}
Lloydie
источник
Если значение wordlong, то при вызове функции может возникнуть много накладных расходов. Вы можете оптимизировать это путем передачи word, targetи в replacementкачестве константных ссылок.
TrebledJ
2

Как насчет Abseil StrReplaceAll ? Из заголовочного файла:

// 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;"}});
hotblack944
источник
1

Старая школа :-)

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;

Результат:

H: \ RECURSOS \ аудио \ YouTube \ НЬге \ Falta \

Иван Родригес
источник
0

Это работает! Я использовал нечто похожее на это для приложения книжного магазина, где инвентарь хранился в CSV (например, в файле .dat). Но в случае с одним символом, то есть заменяющим является только один символ, например '|', он должен быть в двойных кавычках "|" чтобы не скинуть недопустимую конверсию const char.

#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

К сожалению, в настоящее время я использую CentOS, поэтому моя версия компилятора приведена ниже. Версия C ++ (g ++), C ++ 98 по умолчанию:

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.
oOpSgEo
источник
0

Если вы хотите использовать std::strings, вы можете использовать strsubфункцию этого примера приложения как есть или обновить ее, если хотите, чтобы она принимала другой тип или набор параметров для достижения примерно той же цели. По сути, он использует свойства и функциональные возможности, std::stringчтобы быстро удалить соответствующий набор символов и вставить нужные символы непосредственно в std::string. Каждый раз, когда он выполняет эту операцию замены, смещение обновляется, если он все еще может найти совпадающие символы для замены, и, если он не может заменить ничего, он возвращает строку в своем состоянии из последнего обновления.

#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::strings в качестве параметров, чтобы вместо этого можно было передавать строки в стиле C, вы можете увидеть обновленный пример ниже:

#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;
}
kayleeFrye_onDeck
источник
0

Для простых ситуаций это работает довольно хорошо, без использования какой-либо другой библиотеки, кроме std :: string (которая уже используется).

Замените все вхождения символа a символом b в some_string :

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

Если строка большая или несколько вызовов для замены является проблемой, вы можете применить метод, упомянутый в этом ответе: https://stackoverflow.com/a/29752943/3622300

Гуней Озсан
источник
0

Вот решение, которое я выбрал, в духе максимального DRI. он будет искать sNeedle в sHaystack и заменять его на sReplace, nTimes, если не равно 0, иначе все вхождения sNeedle. он не будет искать снова в замененном тексте.

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;
}
не указано
источник