Как повторить строку переменное количество раз в C++?
Я хочу вставить 'n' пробелы (или любую строку) в начале строки в C++. Есть ли прямой способ сделать это, используя std::strings или char* strings?
Например, в Python вы могли бы просто сделать
>>> "." * 5 + "lolcat"
'.....lolcat'
11 ответов
std::string foo = std::string(5, '.') + "lolcat";
Проверьте конструкторы std::string.
Нет прямого идиоматического способа повторения строк в C++, эквивалентного оператору * в Python или оператору x в Perl. Если вы повторяете один символ, конструктор с двумя аргументами (как предлагалось в предыдущих ответах) работает хорошо:
std::string(5, '.')
Это надуманный пример того, как вы можете использовать поток ostring для повторения строки n раз:
#include <sstream>
std::string repeat(int n) {
std::ostringstream os;
for(int i = 0; i < n; i++)
os << "repeat";
return os.str();
}
В зависимости от реализации, это может быть несколько более эффективным, чем просто конкатенация строки n раз.
Используйте одну из форм string::insert:
std::string str("lolcat");
str.insert(0, 5, '.');
Это вставит "....." (пять точек) в начале строки (позиция 0).
Для целей примера, представленного в OP, достаточно stor:: string ctor: 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;
}
Как намекал коммодор Йегер, я не думаю, что какие-либо другие ответы на самом деле отвечают на этот вопрос; вопрос спрашивает, как повторить строку, а не символ.
Хотя ответ, данный Коммодором, является правильным, он довольно неэффективен. Вот более быстрая реализация, идея состоит в том, чтобы минимизировать операции копирования и выделения памяти, сначала экспоненциально увеличивая строку:
#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 раз".
Я знаю, что это старый вопрос, но я пытался сделать то же самое и нашел то, что я считаю более простым решением. Похоже, что cout имеет эту функцию, встроенную в cout.fill(), см. Ссылку для полного объяснения.
http://www.java-samples.com/showtutorial.php?tutorialid=458
cout.width(11);
cout.fill('.');
cout << "lolcat" << endl;
выходы
.....lolcat
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();
}
Вы должны написать свой собственный потоковый манипулятор
cout<< multi (5) << "what" << "lolcat";
Вот пример строки "abc"
repeated
3 раза:
#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <iterator>
using namespace std;
int main() {
ostringstream repeated;
fill_n(ostream_iterator<string>(repeated), 3, string("abc"));
cout << "repeated: " << repeated.str() << endl; // repeated: abcabcabc
return 0;
}
@Daniel предоставил реализацию, которая значительно быстрее, чем другие ответы в своей основной ветке выполнения (где n > 1 и str не пуста). Однако крайние случаи обрабатываются гораздо более неэффективно, чем могли бы быть.
Эта реализация исправляет эти проблемы:
#include <string>
#include <cstddef>
std::string repeat(size_t n, const std::string& str) {
if (n == 0 || str.empty()) return {};
if (n == 1) return str;
const auto period = str.size();
if (period == 1) return std::string(n, str.front());
std::string ret(str);
ret.reserve(period * n);
std::size_t m {2};
for (; m < n; m *= 2) ret += ret;
ret.append(ret.c_str(), (n - (m / 2)) * period);
return ret;
}
Тестовое сравнение двух реализаций на сайте quick-bench.com показывает следующие различия в этих крайних случаях. Clang 13.0 — первое число, а GCC 10.3 — второе. -Оптимизация O3 во всех случаях.
- Для n == 0 эта реализация (9x/11x) быстрее.
- Для str.empty() == true это (2,4x/3,4x) быстрее.
- Для n == 1 и str.size() > 1 это (2,1x/1,4x) быстрее.
- А для str.size() == 1 это (1,3x/1,2x) быстрее.
Проблема оригинальной реализации сводится к передаче в функцию по значению. Это вызывает копию
str
при каждом звонке в
repeat
в некоторых крайних случаях это не нужно; особенно когда n == 0.
Есть еще один способ сделать это с помощью библиотеки диапазонов:
#include <string>
#include <range/v3/all.hpp>
std::string repeat(const std::string& input, size_t num)
{
return ranges::views::repeat_n(input, num) | ranges::views::join(std::string{""}) | ranges::to<std::string>();
}
Я пытался сделать то же самое сstd::ranges
но поддержка этого кажется неполной для всех компиляторов (которые доступны на godbolt), поэтому в настоящее время мы все еще используем в работе range-v3.