Как повторить строку переменное количество раз в C ++?

127

Я хочу вставить пробелы «n» (или любую строку) в начало строки в C ++. Есть ли какой-либо прямой способ сделать это, используя строки std :: strings или char *?

Например, в Python вы можете просто сделать

>>> "." * 5 + "lolcat"
'.....lolcat'

источник
Кто-нибудь предоставит ответ с помощью QString?
Акива

Ответы:

175

В частном случае повторения одного символа вы можете использовать std::string(size_type count, CharT ch):

std::string(5, '.') + "lolcat"

NB. Это не может использоваться для повторения многосимвольных строк.

Люк
источник
80
OP попросил повторить строку, а не символ.
Флориан Кауфманн,
39

Нет прямого идиоматического способа повторения строк в 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 раз.

Коммодор Джагер
источник
17

Используйте одну из форм string :: insert:

std::string str("lolcat");
str.insert(0, 5, '.');

Это вставит "....." (пять точек) в начало строки (позиция 0).

CAMH
источник
16
OP попросил повторить строку, а не символ.
Brent
@Brent OP запросил оба - «n 'пробелов (или любую строку)», а затем продолжил, чтобы продемонстрировать свое намерение с одной точкой в ​​качестве строки. Английский не является родным языком многих людей, поэтому иногда вам нужно угадать их точные требования, и при анализе возникает вопрос, как это сделать с помощью одного символа. Мне очень жаль, что вы нашли мой ответ достаточно бесполезным, и вам нужно было проголосовать против него.
camh
13

Я знаю, что это старый вопрос, но я хотел сделать то же самое и нашел то, что, по моему мнению, является более простым решением. Похоже, что cout имеет эту функцию, встроенную в cout.fill (), см. Ссылку для «полного» объяснения.

http://www.java-samples.com/showtutorial.php?tutorialid=458

cout.width(11);
cout.fill('.');
cout << "lolcat" << endl;

выходы

.....lolcat
Ян
источник
6
только точки: замените последнюю строку на ...cout << "" << endl;
Musefan
9

Для целей примера , приведенного в OP STD :: т е р струны достаточно: std::string(5, '.'). Однако, если кто-то ищет функцию для многократного повторения std :: string:

std::string repeat(const std::string& input, unsigned num)
{
    std::string ret;
    ret.reserve(input.size() * num);
    while (num--)
        ret += input;
    return ret;
}
Павел П.
источник
8

Как намекал коммодор Джагер, я не думаю, что какие-либо другие ответы действительно отвечают на этот вопрос; вопрос спрашивает, как повторить строку, а не символ.

Хотя ответ, данный 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;
    } else if (n == 1 || str.empty()) {
        return str;
    }
    const auto 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:

#include <utility>

std::string operator*(std::string str, std::size_t n)
{
    return repeat(std::move(str), n);
}

На моей машине это примерно в 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

Вы должны написать свой собственный манипулятор потока

cout << multi (5) << "что угодно" << "lolcat";

Roskoto
источник
15
написание манипулятора потока - очень сложный способ сделать что-то очень простое!
Zero
10
C ++ - очень сложный способ делать что-то очень простое.
JDPeckham
5

ITNOA

Для этого вы можете использовать функцию C ++.

 std::string repeat(const std::string& input, size_t num)
 {
    std::ostringstream os;
    std::fill_n(std::ostream_iterator<std::string>(os), num, input);
    return os.str();
 }
sorosh_sabz
источник
1
Что, черт возьми, означает «ITNOA»? Не могу найти упоминания об этом в Интернете.
Фоллинг