Як мені знайти / знайти та замінити у стандартному рядку?


Відповіді:


74

Чому б не застосувати власну заміну?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

3
Ви трохи возиться з пам'яттю тут із усіма викликами "замінити": складність буде n², якщо ви видалите "o" з "ooooooo ... o". Я гадаю, що можна зробити краще, але це рішення полягає в тому, що його легко зрозуміти.
— Zonko

1
Чому це не фактичний цикл for, а не заплутаний цикл for?
— Ширік

Я звик застосовувати принцип „найменшого сюрпризу”. Бо цикли призначені для простого збільшення індексу, більшу частину часу. Тут, на мою думку, цикл while є зрозумілішим.
— yves Baumes

1
@aldo Як загальне правило, краще уникати складності і, наприклад, використовувати регулярний вираз, як згадувалося в інших відповідях. Але залежно від ваших потреб, ви можете контролювати залежності свого проекту. Маленький фрагмент коду, який робить те, що саме вам потрібно, не більше, іноді стає кращим.
— yves Baumes

158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

Ось офіційна документація на replace_all.


1
Зверніть увагу, що вам не потрібно явно створювати std :: string для шаблону та заміни: boost :: replace_all (target, "foo", "bar");
— Alexis Wilke

4
+1, із застереженням: replace_allбуде segfault для версій boost> 1.43 на Sun Studio для будь-якої версії <12.3
— Брайан Ванденберг

3
boostзначно збільшує час компіляції на вбудованих пристроях. Навіть чотириядерний ARMv7. 100 рядків коду компілюється за 2 хвилини, без посилення, 2 секунди.
— Piotr Kula

4
@ppumkin: це означає, що ваш компілятор (або налаштування збірки, або що завгодно) відстійний, а не цільова архітектура, яка не має до цього нічого спільного.
— Даніель Каміль Козар

Якщо ваш компілятор підтримує попередньо скомпільований заголовок, настійно рекомендується використовувати його під час використання boost. Це дійсно економить час.
— Олексій Омельченко

33

У C ++ 11 ви можете зробити це як однолінійний з викликом regex_replace:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

вихід:

Removeallspaces

Дякую, дуже простий у використанні та запам'ятовуванні!
— Джуліан Деклерк

Зауважте також, що це fromможе бути регулярним виразом - так що ви можете використовувати більш складні критерії відповідності, якщо вам потрібно. Чого я не бачу, так це як це зробити, не застосовуючи якусь форму синтаксичного аналізу регулярних виразів - замість цього використовуючи лише пряму інтерпретацію fromсимволів.
— Брент Бредберн,

Для цього може знадобитися сучасний компілятор. Це працювало з gcc 5.0, але у мене були проблеми з gcc 4.8.4.
— Брент Бредберн,

@nobar, так, якщо я добре пам'ятаю, підтримка регулярних виразів у 4.8.x не була повною. Крім того, ви можете мати більш складні пошуки, але ви отримуєте покарання з розумом ... Це буде повільніше, ніж інші, більш прямий пошук і заміна функцій.
— Alexis Wilke

2
Зверніть увагу, що це буде працювати лише для дуже основних буквено-цифрових символів і нічого іншого, не роблячи великої попередньої обробки залежно від типу рядка. Я ще не знайшов заміни рядків на основі регулярних виразів.
— Піюш Соні

17

Чому б не повернути змінений рядок?

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

Якщо вам потрібна продуктивність, ось оптимізована функція, яка змінює вхідний рядок, вона не створює копію рядка:

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

Тести:

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 changed: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

Вихід:

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

6

Мій шаблонізований вбудований функція пошуку та заміни на місці:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

Він повертає підрахунок кількості замінених елементів (для використання, якщо ви хочете послідовно запустити це тощо). Щоб використовувати його:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
Я спробував цей зразок під GCC, але він не скомпілювався - йому не сподобалось використання T :: size_t. Заміна T :: size_t на ім'я типу T :: size_type вирішує проблему.
— Andrew Wyatt,

3

Найпростіший спосіб (запропонувати щось біля написаного вами) - використовувати Boost.Regex , зокрема regex_replace .

std :: string вбудував методи find () і replace (), але вони більш громіздкі в роботі, оскільки вимагають роботи з індексами та довжинами рядків.


3
Існують також алгоритми посилення рядків, включаючи replace_all (регулярний вираз може бути трохи важким для такої простої заміни).
— UncleBens

3

Я вірю, що це спрацює. В якості параметра приймається const char *.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

Враховуючи, що size_typeдля рядка є unsigned, ваша >=перевірка в циклі умови буде завжди true. Ви повинні std::string::nposтам користуватися.
— Павло Мінаєв

size_type не підписаний. Він не підписаний на багатьох платформах, але не на всіх.
— Алан

12
Чому у світі це не частина std :: string? Чи є якийсь інший серйозний клас String у світі програмування, який не пропонує операцію "знайти та замінити"? Звичайно, це частіше, ніж наявність двох ітераторів і бажання замінити текст між собою ?? Іноді std :: string здається автомобілем з лобовим склом з регульованим спектром, але жодним чином не можна скочуватися з вікна водія.
— Spike0xff 02

@ Spike0xff boost hasroll_down_window
— ta.speot.is

1
@gustafr: Моя помилка. Я працював над системами, де старші компілятори неправильно визначали size_t.
— Алан

1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

1
Нам потрібно лише шукати нові збіги з останнього збігу, тому алгоритм ретельно відстежує останній збіг у поз. pos2 завжди зберігає наступний збіг, тому ми об'єднуємо рядок між pos і pos2 до результату, а потім просуваємо pos і pos2. Якщо жодного іншого збігу не вдається знайти, ми об’єднуємо решту отриманого рядка.
— Björn Ganster

1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

Важливою є перевірка наявності порожнього oldStr. Якщо з якоїсь причини цей параметр порожній, ви застрягнете в нескінченному циклі.

Але так, використовуйте перевірений C ++ 11 або рішення Boost, якщо можете.

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