2008-12-12 4 views
36

У меня есть ситуации, когда мне нужно обработать большой (многие ГБ в) объемы данных как таковой:инициализации станд :: строка из полукокса * без копии

  1. построить большую строку путем добавления много меньше (C полукокса *) строки
  2. подрезать строка
  3. преобразовать строку в C++ сопзЬ станд :: строка для обработки (только для чтения)
  4. повтор

ПДР ta на каждой итерации независимы.

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

Есть ли способ конвертировать строку C (char *) в строку stl C++ (std :: string), не требуя, чтобы std :: string внутренне выделяла/копировала данные?

В качестве альтернативы, могу ли я использовать строковые потоки или что-то похожее на повторное использование большого буфера?

Edit: Спасибо за ответы, для ясности, я думаю, что пересмотренный вопрос будет:

Как можно эффективно построить (через постоянные добавления) на C++ строку СТЛ. И если выполнение этого действия в цикле, где каждый цикл полностью независим, как я могу повторно использовать это выделенное пространство.

ответ

17

Возможно ли использовать строку C++ на шаге 1? Если вы используете string::reserve(size_t), вы можете выделить достаточно большой буфер для предотвращения множественных распределений кучи при добавлении меньших строк, а затем вы можете просто использовать ту же самую строку C++ на всех остальных этапах.

См. this link для получения дополнительной информации о функции reserve.

1

Является ли каждая итерация независимой, чтобы вы могли использовать одну и ту же строку std :: для каждой итерации? Можно было бы надеяться, что ваша реализация std :: string будет достаточно умна для повторного использования памяти, если вы присвоите ей const char *, когда она была ранее использована для чего-то другого.

Назначение символа char * в строку std :: всегда должно как минимум копировать данные. Управление памятью является одной из основных причин использования std :: string, поэтому вы не сможете ее переопределить.

7

Чтобы справиться с действительно большими строками, SGI имеет класс Rope в своем STL.
Нестандартный, но может быть полезным.

http://www.sgi.com/tech/stl/Rope.html

Видимо веревка в следующей версии стандарта :-)
Примечание разработчика шутка. Канат - большая строка. (Ha Ha) :-)

18

Вы не можете на самом деле сформировать std :: string без копирования данных. Строковый поток, вероятно, повторно использовал бы память из прохода, чтобы пройти (хотя я думаю, что в стандарте не говорится о том, нужно ли это на самом деле), но он все равно не избежал бы копирования.

Общим подходом к этой проблеме является запись кода, который обрабатывает данные на шаге 3, для использования пары итератора begin/end; то он может легко обрабатывать либо std :: string, вектор символов, пару исходных указателей и т. д. В отличие от передачи его типа контейнера, такого как std :: string, он больше не будет знать и не заботится о том, как распределялась память, поскольку он все равно будет принадлежать вызывающему. Выполнение этой идеи до логического завершения - boost::range, которая добавляет все перегруженные конструкторы, чтобы позволить вызывающему только передать строку/вектор/список/контейнер любого типа с .begin() и .end() или отдельными итераторами.

Написав код обработки для работы на произвольном диапазоне итераторов, вы могли бы даже написать пользовательский итератор (не так сложно, как кажется, в основном просто объект с некоторыми стандартными typedefs и operator ++/*/=/== /! = перегружен, чтобы получить итератор с прямым доступом), который заботится о том, чтобы перейти к следующему фрагменту каждый раз, когда он попал в конец того, над которым он работает, пропуская пробелы (я предполагаю, что это то, что вы имели в виду под обрезкой) , То, что вам никогда не приходилось собирать целую цепочку со всех сторон. Независимо от того, будет ли это выигрыш, зависит от того, сколько фрагментов/сколько у вас фрагментов. Это, по сути, то, что канат SGI, упомянутый Мартином Йорком, - это: строка, в которой append образует связанный список фрагментов, а не смежный буфер, что, таким образом, подходит для гораздо более длинных значений.


UPDATE (так как я все еще вижу иногда upvotes на этот ответ):

C++ 17 вводит другой выбор: std::string_view, который заменил зЬй :: строку во многих сигнатуры функций, не является - привязка ссылки к символьным данным. Он неявно конвертируется из std :: string, но также может быть явно сконструирован из смежных данных, находящихся в другом месте, избегая ненужного копирования std :: string.

+0

Я думаю, что ваше решение - лучший подход (chaning для кода обработки), к сожалению, в этой ситуации это не вариант. – Akusete 2008-12-12 02:54:42

+1

Существует ли стандартный способ для повторного использования буфера? Я просто не хочу полагаться на реализацию на конкретной платформе. – Akusete 2008-12-12 02:56:26

+0

Если этот код обработки не является библиотечной функцией, которая не использует итераторы или строки, просто простой старый символ `char *` +. – SasQ 2015-06-11 06:48:12

0

В этом случае лучше обработать char * напрямую, а не назначать его std :: string.

4

Это ответ на боковое мышление, а не прямое решение вопроса, а «размышление» вокруг него. Может быть полезно, возможно, не ...

Считываемая обработка строки std :: string действительно не требует очень сложного подмножества функций std :: string. Есть ли вероятность, что вы можете выполнить поиск/замену кода, который выполняет всю обработку в std :: strings, чтобы вместо этого потребовался какой-то другой тип? Начать с пустого класса:

класс lightweight_string {};

Затем замените все ссылки std :: string на lightweight_string. Выполните компиляцию, чтобы точно определить, какие операции необходимы для lightweight_string, чтобы она стала заменой. Затем вы можете сделать свою реализацию, как хотите.

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