С++ заменяет несколько строк в строке за один проход

Учитывая следующую строку, "Hi ~+ and ^*. Is ^* still flying around ~+?"

Я хочу заменить все вхождения "~+" и "^*" на "Bobby" и "Danny", поэтому строка будет:

"Hi Bobby and Danny. Is Danny still flying around Bobby?"

Я бы предпочел не требовать двойную функцию замены Boost, чтобы заменить вхождения двух разных значений.

Ответы

Ответ 1

Мне удалось реализовать требуемую функцию замены с помощью Boost.Iostreams. В частности, метод, который я использовал, был потоком фильтрации, использующим регулярное выражение для соответствия тому, что нужно заменить. Я не уверен в производительности файлов размером с гигабайт. Конечно, вам нужно будет протестировать его. В любом случае, здесь код:

#include <boost/regex.hpp>
#include <boost/iostreams/filter/regex.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>

int main()
{
   using namespace boost::iostreams;

   regex_filter filter1(boost::regex("~\\+"), "Bobby");
   regex_filter filter2(boost::regex("\\^\\*"), "Danny");

   filtering_ostream out;
   out.push(filter1);
   out.push(filter2);
   out.push(std::cout);

   out << "Hi ~+ and ^*. Is ^* still flying around ~+?" << std::endl;

   // for file conversion, use this line instead:
   //out << std::cin.rdbuf();
}

Вышеприведенные рисунки "Hi Bobby and Danny. Is Danny still flying around Bobby?" при запуске, как и ожидалось.

Было бы интересно увидеть результаты работы, если вы решите его измерить.

Daniel

Редактирование: я просто понял, что regex_filter необходимо прочитать всю последовательность символов в памяти, что делает ее довольно бесполезной для ввода в формате гигабайта. О, хорошо...

Ответ 2

Я заметил, что это был год с тех пор, как это было активно, но для чего это стоит. Сегодня я столкнулся с статьей о CodeProject, которая утверждает, что решает эту проблему - возможно, вы можете использовать идеи оттуда:

Я не могу ручаться за его правильность, но, возможно, стоит взглянуть на нее.:)

Реализация, безусловно, требует хранения всей строки в памяти, но вы можете легко обойти это (как и в любой другой реализации, которая выполняет замены), если вы можете разделить входные данные на блоки и гарантировать, что вы никогда не разделяете на которое находится внутри символа, который нужно заменить. (Один простой способ сделать это в вашем случае - разбить в том месте, где следующий char не является символом, используемым в символе.)

-

В моей книге есть причина, отличная от производительности (хотя это достаточная причина в моей книге) добавить метод "ReplaceMultiple" в одну библиотеку строк: просто выполнение операции замены N раз НЕ является правильным вообще.

Если значения, которые заменяют символы, не ограничены, в конечном итоге значения могут обрабатываться как символы в последующих операциях замены. (Могут быть ситуации, когда вы действительно этого хотите, но в определенных случаях вы не делаете этого. Использование странных символов уменьшает серьезность проблемы, но не решает ее, и "является уродливым", поскольку строки, которые должны быть отформатированы, могут быть определяемыми пользователем - и поэтому не должны требовать экзотических символов.)

Однако, я подозреваю, что есть веская причина, по которой я не могу легко найти общую реализацию с несколькими заменами. Операция "ReplaceMultiple" просто не является (очевидно) четко определенной в целом.

Чтобы увидеть это, рассмотрите, что это означает "заменить" aa "на"! "и 'baa' с '?' в строке" abaa "? Является результатом" ab!" или 'a?' - или такая замена незаконна?

Можно потребовать, чтобы символы были "префиксами", но во многих случаях это было бы неприемлемо. Скажем, я хочу использовать это для форматирования текста шаблона. И скажите, что мой шаблон предназначен для кода. Я хочу заменить "§ table" именем таблицы базы данных, известным только во время выполнения. Было бы обидно, если бы я теперь не мог использовать "§t" в том же шаблоне. Шаблонный script может быть чем-то совершенно общим, и в одно мгновение я сталкиваюсь с клиентом, который фактически использовал "§" в своих именах таблиц... потенциально делая мою библиотеку шаблонов более менее полезной.

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

Ответ 3

У Boost string_algo есть функция replace_all. Вы можете использовать это.

Ответ 4

Я предлагаю использовать библиотеку Boost Format. Вместо ~+ и ^* вы затем используете %1% и %2% и т.д. Несколько более систематически.

Пример из документов:

cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50; 
     // prints "writing toto,  x=40.230 : 50-th try"

Приветствия и hth.,

- Alf

Ответ 5

Я бы предложил использовать std:: map. Таким образом, у вас есть набор замен, так что:

std::map<std::string,std::string> replace;
replace["~+"]=Bobby;
replace["^*"]=Danny;

Затем вы можете поместить строку в вектор строк и проверить, будет ли каждая строка встречаться на карте, и если она ее заменит, вам также необходимо снять любые знаки препинания с конца. Или добавьте их в замену. Вы могли бы сделать это в одном цикле. Я не уверен, что это действительно более эффективно или полезно, чем повысить.