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


Відповіді:


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
На жаль, це дозволяє замінити лише одне char на інше. Він не може замінити знаку більше символів (тобто рядка). Чи є спосіб зробити пошук-заміну на більше символів?
SasQ

6
@Kirill В. Лядвінський Що робити, якщо я просто хочу видалити подію.
SIFE

4
@ KirillV.Lyadvinsky: Коли я використовую цей метод для заміни всіх x на y, результат - довга рядок y, незалежно від того, що є початковою рядком. Мені цікаво, на вашу думку, проблема. (код точно такий, як ви писали)
Трансцендент

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

Тоді вам не вистачає декількох -Iпрапорів для вашого компілятора, щоб він міг знайти бібліотеки Boost у вашій системі. Можливо, спочатку потрібно навіть встановити його.
Мартін Удінг

Вищезазначене є більш ефективним, оскільки воно виходить із std lib.No all using boost library ;-)
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 :: substitu () буде називатися багато разів, що викликає безліч копій рядків. Дивіться моє рішення, яке вирішує цю проблему.
мінастарос

1
Ніт вибирається вперед: за адресою => за посиланням . Незалежно від того, чи це адреса чи ні - це детальна інформація про реалізацію.
Макс Трукса

1
Справді слід перевірити, чи fromрядок порожній, інакше відбудеться нескінченний цикл.
новачок

34

Уявіть собі велику двійкову крапку, де всі 0x00 байтів заміняться на "\ 1 \ x30", а всі 0x01 байт на "\ 1 \ x31", оскільки транспортний протокол не має \ 0-байт.

У випадках, коли:

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

надані рішення не можуть бути застосовані (оскільки вони замінюють лише окремі символи) або мають проблеми з продуктивністю, оскільки вони будуть викликати рядок :: замінювати кілька разів, що генерує копії розміру блобу знову і знову. (Я не знаю прискореного рішення, можливо, з цієї точки зору це нормально)

Це один ходить за всіма входжень в заданій стрічці і створює новий рядок по частинах один раз :

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

Це, безумовно, найкраще рішення, яке будується тільки на 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К або менше.
ТЕД

3
... якщо говорити, мені подобається метод Кирила краще (і він вже проголосував за нього).
ТЕД

Що станеться, якщо "x" не знайдено? Крім того, чому ви використовуєте подвійні брекети?
Prasath Govind

@PrasathGovind - я просто показував необхідні дзвінки (отже, "щось на кшталт"). Важливі, але незрозумілі деталі, такі як правильне поводження з помилками, залишалися вправою для читача. Що стосується "подвійних брекетів", я не впевнений, що це, або про що ви говорите. Для мене "дужка" - це {характер. Я не знаю, що таке "подвійний брекет". Можливо, у вас є якась проблема з шрифтом?
ТЕД

6

Якщо ви хочете замінити більше одного символу і маєте справу лише з цим std::string, цей фрагмент спрацює, замінивши sNeedle в sHaystack на sReplace, а sNeedle і sReplace не повинні бути однакового розміру. Ця програма використовує цикл while для заміни всіх подій, а не лише першого, знайденого зліва направо.

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

Це О (п ^). Ви могли це зробити в O (n) час.
Changming Sun

3
@ChangmingSun, про яке (О) рішення ви маєте на увазі?
хабакук

2
Це буде нескінченним циклом, якщо kNeedle буде підрядкою sReplace.
гордість

Плюс є findдзвінок двічі. Розглянемо, як зробити цей результат тимчасовою змінною.
Люк Блум

4

Як запропонував Кирилл, або використовуйте метод заміни або повторіть уздовж рядка, замінюючи кожну таблицю незалежно.

Крім того, ви можете використовувати findметод або find_first_ofзалежно від того, що вам потрібно зробити. Жодне з цих рішень не виконає роботу за один раз, але за допомогою декількох додаткових рядків коду ви повинні змусити їх працювати для вас. :-)


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

Якщо wordдовгий, під час виклику функції може виникнути велика кількість накладних витрат. Ви можете оптимізувати це шляхом передачі 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;"}});

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 \ audio \ youtube \ libre \ falta \


0

Це працює! Я використовував щось подібне до цього для книжкового додатка, де інвентар зберігався в CSV (наприклад, файл .dat). Але у випадку одного символу, що означає, що замінювач є лише одним знаком, наприклад, "|", він повинен бути у подвійних лапках "|" щоб не кидати недійсну таблицю const конверсії.

#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.

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

0

У простих ситуаціях це працює досить добре, не використовуючи жодної іншої бібліотеки, тоді std :: string (яка вже використовується).

Замініть всі випадки виникнення символу a символом b в якомусь строці :

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.