C ++ заменяет несколько строк в строке за один проход - PullRequest
16 голосов
/ 09 октября 2010

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

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

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

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

Ответы [ 5 ]

7 голосов
/ 09 октября 2010

Мне удалось реализовать требуемую функцию замены с помощью 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?", как и ожидалось.

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

Даниэль

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

3 голосов
/ 13 января 2012

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

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

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

-

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

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

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

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

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

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

0 голосов
/ 09 октября 2010

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

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

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

0 голосов
/ 09 октября 2010

Я предлагаю использовать библиотеку 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"

Cheershth.,

- Alf

0 голосов
/ 09 октября 2010

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

...