Как искать / находить и заменять в стандартной строке?

Ответы:

75

Почему бы не реализовать собственную замену?

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
Здесь вы немного запутались с памятью со всеми вызовами "replace": сложность будет n², если вы удалите "o" из "ooooooo ... o". Я думаю, можно добиться большего, но это решение имеет то преимущество, что его легко понять.
Zonko
1
Почему это не настоящий цикл for, а не запутанный цикл for?
Ширик
Я привык применять принцип «наименьшего удивления». Циклы For в большинстве случаев используются для простого увеличения индекса. Здесь, на мой взгляд, понятнее цикл while.
Ив Бом
1
@aldo Как правило, лучше избегать сложности и, например, использовать регулярное выражение, как упоминалось в других ответах. Но в зависимости от ваших потребностей вы можете контролировать зависимости вашего проекта. Небольшой фрагмент кода, который делает именно то, что вам нужно, не более того, иногда лучше.
Ив Бомес
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.

TheNamelessOne
источник
1
Обратите внимание, что вам не нужно явно создавать std :: string для шаблона и замены: boost :: replace_all (target, "foo", "bar");
Alexis Wilke
4
+1, с оговоркой: replace_allдля версий повышения> 1.43 в Sun Studio для любой версии <12.3 будет отключен сегмент
Брайан Ванденберг,
3
boostзначительно увеличивает время компиляции на встроенных устройствах. Даже четырехъядерный ARMv7. 100 строк кода компилируются за 2 минуты, без ускорения за 2 секунды.
Петр Кула
4
@ppumkin: это означает, что ваш компилятор (или настройка сборки, или что-то еще) отстой, а не целевая архитектура, которая не имеет к этому никакого отношения.
Daniel Kamil Kozar
Если ваш компилятор поддерживает предварительно скомпилированный заголовок, настоятельно рекомендуется использовать его при использовании 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.
Brent Bradburn
@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 на typename T :: size_type устраняет проблему.
Эндрю Вятт
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 имеетroll_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
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.

эриккуртин
источник