2010-10-08 3 views
15

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

Я хочу, чтобы заменить все вхождения "~+" и "^*" с «Бобби» и «Дэнни», так что строка будет выглядеть так:

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

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

+0

Откуда взялась строка? – JoshD

+0

Как мы знаем, 'O (n) + O (n)' все еще 'O (n)', в чем тут мотивация * real *? – Arun

+1

Фактическая строка/данные потенциально составляет 100 с гигабайт в размере, обрабатывается поэтапно, а при вызове замены дважды все еще O (n), n довольно большой. –

ответ

0

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

+0

Спасибо Мэтью, но для этого требуется только одно значение, и мне нужно будет его дважды называть. Я пытаюсь выяснить, есть ли способ предоставить по существу карту, если вы найдете x1 заменить на y1 и x2 на y2 и так далее, и сканирование по строке произойдет только один раз. –

0

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

- Альф

+0

Спасибо за предложение Alf, однако я не контролирую входные данные, поэтому ваше предложение не будет работать. Мне нужно обработать содержимое и изменить значения в пределах, указанных пользователем, который вызывает код. –

5

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

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

0

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

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

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

3

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

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

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

-

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

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

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

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

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

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

Смежные вопросы