Як повторити рядок змінну кількість разів у C ++?


127

Я хочу вставити пробіли 'n' (або будь-яку рядок) на початку рядка в C ++. Чи є прямий спосіб це зробити, використовуючи std :: strings або char * string?

Наприклад, у Python ви могли просто зробити

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

Хтось надає відповідь за допомогою QString?
Аківа

Відповіді:


175

У конкретному випадку повторення одного символу ви можете використовувати std::string(size_type count, CharT ch):

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

NB. Це не можна використовувати для повторення рядків з декількома символами.


79
ОП просила повторити рядок, а не символ.
Флоріан Кауфман

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

Використовуйте одну з форм рядка :: insert:

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

Тут буде вставлено "....." (п'ять крапок) на початку рядка (позиція 0).


15
ОП просила повторити рядок, а не символ.
Брент

@Brent ОП запитувало обидва - пробіли "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

Як наголошував Комодор Йегер, я не думаю, що жодна з інших відповідей насправді не відповідає на це запитання; питання задає питання, як повторити рядок, а не символ.

Хоча відповідь Комодора правильна, вона досить неефективна. Ось більш швидка реалізація, ідея полягає в тому, щоб мінімізувати операції копіювання та розподілу пам'яті, спочатку експоненціально зростаючи рядок:

#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 раз" .


Ваша реалізація не "мінімізує копіювання". Зверніть увагу, що +=внутрішній цикл для вашого циклу також має певний цикл, який робить str.size()ітерації. str.size()зростає в кожній ітерації зовнішньої петлі, тому після кожної зовнішньої ітерації внутрішня петля повинна робити більше ітерацій. Ваша та наївна реалізація "копіювати n разів" загалом як n * periodсимволи копії . Ваша реалізація здійснює лише один розподіл пам'яті через початкове reserve. Я думаю, ви профілювали свою реалізацію досить малим strі великим n, але не також великим strі малим n.
Флоріан Кауфман

@FlorianKaufmann Не впевнений, чому ти вирішив напасти на мою відповідь. Але під "мінімізацією копіювання" я маю на увазі "операції з копіювання". Ідея полягає в тому, що копіювання невеликої кількості великих блоків є більш ефективним (з різних причин), ніж копіювання великої кількості маленьких блоків. Я потенційно уникаю додаткового розподілу на вхідному рядку над наївним методом.
Даніель

2
Це був коментар, в якому зазначалося, що я не вірю вашим твердженням, що ваше рішення набагато краще з точки зору ефективності, ніж наївне рішення. У моїх вимірах, порівняно з наївним рішенням, ваш код швидший з невеликими рядками та багатьма повторами, але повільніше з довгими рядками та кількома повторами. Чи можете ви надати посилання, що пояснюють більш детально різноманітність причин, чому копіювання декількох великих блоків має більш високу ефективність, ніж копіювання багатьох маленьких блоків? Я можу подумати про галузеве передбачення. Щодо кешу процесора, я не впевнений, який варіант є кращим.
Флоріан Кауфман

@FlorianKaufmann Я не бачу суттєвої різниці між великими strта малими nміж двома підходами. Я вважаю, що це більше стосується загального конвеєрним проектом, ніж передбачення галузей, але також слід врахувати питання вирівнювання даних . Вам слід задати нове запитання, щоб дізнатись, чому це більш зручно для процесора / пам'яті, я впевнений, що він би зацікавив і отримав кращу відповідь, ніж я можу дати тут.
Даніель

1
@FlorianKaufmann: На x86 - rep movsbце один із найефективніших способів копіювання, принаймні для середніх та великих копій. Її мікрокодована реалізація має деякий майже постійний накладний запуск (як для AMD, так і для Intel), наприклад, на Sandybridge, від 15 до 40 циклів, плюс 4 цикли на 64B кеш-лінії (найкращий випадок) . Для невеликих копій цикл SSE найкращий, оскільки він не має накладних витрат. Але тоді це підлягає неправильним прогнозам.
Пітер Кордес

6

Ви повинні написати власний маніпулятор потоку

cout << multi (5) << "що завгодно" << "lolcat";


15
написання маніпулятора потоку - це дуже складний спосіб зробити щось дуже просте!
Нуль

10
C ++ - це дуже складний спосіб зробити щось дуже просте.
JDPeckham

5

ІТНОА

Для цього можна використовувати функцію 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();
 }

1
Що на землі означає "ITNOA"? В Інтернеті не можна знайти жодної посилання на нього.
Фоллінг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.