Как повторить строку переменное количество раз в C ++?
127
Я хочу вставить пробелы «n» (или любую строку) в начало строки в C ++. Есть ли какой-либо прямой способ сделать это, используя строки std :: strings или char *?
Нет прямого идиоматического способа повторения строк в C ++, эквивалентного оператору * в Python или оператору x в Perl. Если вы повторяете один символ, конструктор с двумя аргументами (как было предложено в предыдущих ответах) работает хорошо:
std::string(5,'.')
Это надуманный пример того, как вы можете использовать ostringstream для повторения строки n раз:
#include<sstream>
std::string repeat(int n){
std::ostringstream os;for(int i =0; i < n; i++)
os <<"repeat";return os.str();}
В зависимости от реализации это может быть немного эффективнее простого конкатенации строки n раз.
@Brent OP запросил оба - «n 'пробелов (или любую строку)», а затем продолжил, чтобы продемонстрировать свое намерение с одной точкой в качестве строки. Английский не является родным языком многих людей, поэтому иногда вам нужно угадать их точные требования, и при анализе возникает вопрос, как это сделать с помощью одного символа. Мне очень жаль, что вы нашли мой ответ достаточно бесполезным, и вам нужно было проголосовать против него.
camh
13
Я знаю, что это старый вопрос, но я хотел сделать то же самое и нашел то, что, по моему мнению, является более простым решением. Похоже, что cout имеет эту функцию, встроенную в cout.fill (), см. Ссылку для «полного» объяснения.
только точки: замените последнюю строку на ...cout << "" << endl;
Musefan
9
Для целей примера , приведенного в OP STD :: т е р струны достаточно: std::string(5, '.'). Однако, если кто-то ищет функцию для многократного повторения std :: string:
Как намекал коммодор Джагер, я не думаю, что какие-либо другие ответы действительно отвечают на этот вопрос; вопрос спрашивает, как повторить строку, а не символ.
Хотя ответ, данный Commodore, правильный, он довольно неэффективен. Вот более быстрая реализация, идея состоит в том, чтобы минимизировать операции копирования и выделения памяти, сначала экспоненциально увеличивая строку:
#include<string>#include<cstddef>
std::string repeat(std::string str,const std::size_t n){if(n ==0){
str.clear();
str.shrink_to_fit();return str;}elseif(n ==1|| str.empty()){return str;}constauto period = str.size();if(period ==1){
str.append(n -1, str.front());return str;}
str.reserve(period * n);
std::size_t m {2};for(; m < n; m *=2) str += str;
str.append(str.c_str(),(n -(m /2))* period);return str;}
Мы также можем определить, operator*чтобы приблизиться к версии Python:
На моей машине это примерно в 10 раз быстрее, чем реализация, предоставленная Commodore, и примерно в 2 раза быстрее, чем наивное решение «добавить n - 1 раз» .
Ваша реализация не сводит к минимуму копирование. Имейте в виду, что +=внутри вашего цикла for также есть какой-то цикл, который выполняет str.size()итерации. str.size()растет на каждой итерации внешнего цикла, поэтому после каждой внешней итерации внутренний цикл должен делать больше итераций. Ваша и наивная реализация «копировать n раз» в сумме оба копируют n * periodсимволы. Ваша реализация делает только одно выделение памяти из-за начального reserve. Я предполагаю, что вы профилировали свою реализацию с помощью довольно маленького strи большого n, но не одновременно с большим strи маленьким n.
Флориан Кауфманн,
@FlorianKaufmann Не уверен, почему вы решили атаковать мой ответ. Но под «минимизацией копирования» я подразумеваю «операции копирования». Идея состоит в том, что копирование небольшого количества больших блоков более эффективно (по разным причинам), чем копирование большого количества маленьких блоков. Я потенциально избегаю дополнительного выделения во входной строке по сравнению с наивным методом.
Daniel
2
Это был комментарий, в котором говорилось, что я не верю вашему утверждению, что ваше решение намного лучше с точки зрения эффективности, чем наивное решение. По моим измерениям, по сравнению с наивным решением, ваш код быстрее работает с небольшими строками и большим количеством повторений, но медленнее с длинными строками и небольшим количеством повторений. Не могли бы вы предоставить ссылки, более подробно объясняющие множество причин, по которым копирование нескольких больших блоков имеет более высокую производительность, чем копирование множества маленьких блоков? Я могу думать о предсказании ветвления. Что касается кеша процессора, я не уверен, какой вариант предпочтительнее.
Флориан Кауфманн,
@FlorianKaufmann Я не вижу существенной разницы между двумя подходами для большого strи малого n. Я считаю, что это больше связано с общим конвейером, чем с прогнозированием ветвления как таковым, есть также проблемы с выравниванием данных, которые следует учитывать. Вы должны задать новый вопрос о том, почему это более дружественно к процессору / памяти, я уверен, что это вызовет большой интерес и получит лучший ответ, чем я могу дать здесь.
Daniel
1
@FlorianKaufmann: На x86 rep movsbэто один из наиболее эффективных способов копирования, по крайней мере, для средних и больших копий. Его микрокодированная реализация имеет некоторые почти постоянные накладные расходы на запуск (как на AMD, так и на Intel), например, на Sandybridge, от ~ 15 до 40 циклов, плюс 4 цикла на строку кэша размером 64 ББ (лучший случай) . Для небольших копий лучше всего подходит цикл SSE, поскольку он не требует дополнительных затрат на запуск. Но тогда это может быть ошибочным предсказанием ветвей.
Питер Кордес
6
Вы должны написать свой собственный манипулятор потока
Ответы:
В частном случае повторения одного символа вы можете использовать
std::string(size_type count, CharT ch)
:NB. Это не может использоваться для повторения многосимвольных строк.
источник
Нет прямого идиоматического способа повторения строк в C ++, эквивалентного оператору * в Python или оператору x в Perl. Если вы повторяете один символ, конструктор с двумя аргументами (как было предложено в предыдущих ответах) работает хорошо:
Это надуманный пример того, как вы можете использовать ostringstream для повторения строки n раз:
В зависимости от реализации это может быть немного эффективнее простого конкатенации строки n раз.
источник
Используйте одну из форм string :: insert:
Это вставит "....." (пять точек) в начало строки (позиция 0).
источник
Я знаю, что это старый вопрос, но я хотел сделать то же самое и нашел то, что, по моему мнению, является более простым решением. Похоже, что cout имеет эту функцию, встроенную в cout.fill (), см. Ссылку для «полного» объяснения.
http://www.java-samples.com/showtutorial.php?tutorialid=458
выходы
источник
cout << "" << endl;
Для целей примера , приведенного в OP STD :: т е р струны достаточно:
std::string(5, '.')
. Однако, если кто-то ищет функцию для многократного повторения std :: string:источник
Как намекал коммодор Джагер, я не думаю, что какие-либо другие ответы действительно отвечают на этот вопрос; вопрос спрашивает, как повторить строку, а не символ.
Хотя ответ, данный Commodore, правильный, он довольно неэффективен. Вот более быстрая реализация, идея состоит в том, чтобы минимизировать операции копирования и выделения памяти, сначала экспоненциально увеличивая строку:
Мы также можем определить,
operator*
чтобы приблизиться к версии Python:На моей машине это примерно в 10 раз быстрее, чем реализация, предоставленная Commodore, и примерно в 2 раза быстрее, чем наивное решение «добавить n - 1 раз» .
источник
+=
внутри вашего цикла for также есть какой-то цикл, который выполняетstr.size()
итерации.str.size()
растет на каждой итерации внешнего цикла, поэтому после каждой внешней итерации внутренний цикл должен делать больше итераций. Ваша и наивная реализация «копировать n раз» в сумме оба копируютn * period
символы. Ваша реализация делает только одно выделение памяти из-за начальногоreserve
. Я предполагаю, что вы профилировали свою реализацию с помощью довольно маленькогоstr
и большогоn
, но не одновременно с большимstr
и маленькимn
.str
и малогоn
. Я считаю, что это больше связано с общим конвейером, чем с прогнозированием ветвления как таковым, есть также проблемы с выравниванием данных, которые следует учитывать. Вы должны задать новый вопрос о том, почему это более дружественно к процессору / памяти, я уверен, что это вызовет большой интерес и получит лучший ответ, чем я могу дать здесь.rep movsb
это один из наиболее эффективных способов копирования, по крайней мере, для средних и больших копий. Его микрокодированная реализация имеет некоторые почти постоянные накладные расходы на запуск (как на AMD, так и на Intel), например, на Sandybridge, от ~ 15 до 40 циклов, плюс 4 цикла на строку кэша размером 64 ББ (лучший случай) . Для небольших копий лучше всего подходит цикл SSE, поскольку он не требует дополнительных затрат на запуск. Но тогда это может быть ошибочным предсказанием ветвей.Вы должны написать свой собственный манипулятор потока
источник
ITNOA
Для этого вы можете использовать функцию C ++.
источник