С++ заменяет несколько строк в строке за один проход
Учитывая следующую строку, "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;
Затем вы можете поместить строку в вектор строк и проверить, будет ли каждая строка встречаться на карте, и если она ее заменит, вам также необходимо снять любые знаки препинания с конца. Или добавьте их в замену. Вы могли бы сделать это в одном цикле. Я не уверен, что это действительно более эффективно или полезно, чем повысить.