Wie kann man einen String in C ++ eine variable Anzahl von Malen wiederholen?

127

Ich möchte 'n' Leerzeichen (oder eine beliebige Zeichenfolge) am Anfang einer Zeichenfolge in C ++ einfügen. Gibt es eine direkte Möglichkeit, dies mit std :: strings oder char * strings zu tun?

ZB in Python könnte man das einfach machen

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

quelle
Jemand eine Antwort mit QString geben?
Akiva

Antworten:

175

Im speziellen Fall der Wiederholung eines einzelnen Zeichens können Sie Folgendes verwenden std::string(size_type count, CharT ch):

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

NB. Dies kann nicht zum Wiederholen von Zeichenfolgen mit mehreren Zeichen verwendet werden.

Luke
quelle
79
Das OP bat darum, eine Zeichenfolge und kein Zeichen zu wiederholen.
Florian Kaufmann
39

Es gibt keine direkte idiomatische Möglichkeit, Zeichenfolgen in C ++ zu wiederholen, die dem Operator * in Python oder dem Operator x in Perl entsprechen. Wenn Sie ein einzelnes Zeichen wiederholen, funktioniert der Konstruktor mit zwei Argumenten (wie in den vorherigen Antworten vorgeschlagen) gut:

std::string(5, '.')

Dies ist ein erfundenes Beispiel dafür, wie Sie einen Ostringstream verwenden können, um einen String n-mal zu wiederholen:

#include <sstream>

std::string repeat(int n) {
    std::ostringstream os;
    for(int i = 0; i < n; i++)
        os << "repeat";
    return os.str();
}

Abhängig von der Implementierung kann dies etwas effizienter sein, als die Zeichenfolge n-mal einfach zu verketten.

Commodore Jaeger
quelle
17

Verwenden Sie eine der Formen von string :: insert:

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

Dadurch wird am Anfang der Zeichenfolge (Position 0) "....." (fünf Punkte) eingefügt.

camh
quelle
15
Das OP bat darum, eine Zeichenfolge und kein Zeichen zu wiederholen.
Brent
@Brent Das OP fragte nach beiden - "'n' Leerzeichen (oder einer beliebigen Zeichenfolge)" und demonstrierte dann ihre Absicht mit einem einzelnen Punkt als Zeichenfolge. Englisch ist nicht die Muttersprache vieler Menschen, daher müssen Sie manchmal ihre genauen Anforderungen erraten. Bei der Analyse stellt sich die Frage, wie dies mit einem einzelnen Zeichen geschehen soll. Es tut mir leid, dass Sie meine Antwort als nicht hilfreich genug empfunden haben, um sie abzustimmen.
Camh
13

Ich weiß, dass dies eine alte Frage ist, aber ich wollte das Gleiche tun und habe eine meiner Meinung nach einfachere Lösung gefunden. Es scheint, dass cout diese Funktion mit cout.fill () integriert hat. Eine vollständige Erklärung finden Sie unter dem Link

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

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

Ausgänge

.....lolcat
Ian
quelle
6
nur Punkte: letzte Zeile ändern in ...cout << "" << endl;
Musefan
9

Für die Zwecke des vom OP bereitgestellten Beispiels reicht der ctor von std :: string aus : std::string(5, '.'). Wenn jedoch jemand nach einer Funktion sucht, mit der std :: string mehrmals wiederholt werden kann:

std::string repeat(const std::string& input, unsigned num)
{
    std::string ret;
    ret.reserve(input.size() * num);
    while (num--)
        ret += input;
    return ret;
}
Pavel P.
quelle
8

Wie Commodore Jaeger anspielte, glaube ich nicht, dass eine der anderen Antworten diese Frage tatsächlich beantwortet; In der Frage wird gefragt, wie eine Zeichenfolge und kein Zeichen wiederholt werden soll.

Die Antwort von Commodore ist zwar richtig, aber ziemlich ineffizient. Hier ist eine schnellere Implementierung. Die Idee ist, Kopiervorgänge und Speicherzuweisungen zu minimieren, indem zuerst die Zeichenfolge exponentiell vergrößert wird:

#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;
}

Wir können auch ein definieren operator*, um der Python-Version etwas näher zu kommen:

#include <utility>

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

Auf meinem Computer ist dies ungefähr 10x schneller als die von Commodore bereitgestellte Implementierung und ungefähr 2x schneller als eine naive Lösung, die n-1-mal anhängt.

Daniel
quelle
Ihre Implementierung minimiert das Kopieren nicht. Beachten Sie, dass die +=interne Schleife in Ihrer for-Schleife auch eine Art Schleife enthält, die str.size()Iterationen ausführt. str.size()wächst in jeder Iteration der äußeren Schleife, sodass die innere Schleife nach jeder Iteration der äußeren Schleife mehr Iterationen durchführen muss. Ihre und die naive 'n-malige' Implementierung implementieren insgesamt beide Kopierzeichen n * period. Ihre Implementierung führt aufgrund der Initiale nur eine Speicherzuweisung durch reserve. Ich denke, Sie haben Ihre Implementierung mit einem eher kleinen strund einem großen Profil versehen n, aber nicht auch mit einem großen strund einem kleinen n.
Florian Kaufmann
@FlorianKaufmann Ich bin mir nicht sicher, warum du meine Antwort angegriffen hast. Aber mit "Kopieren minimieren" meine ich "Kopiervorgänge". Die Idee ist, dass das Kopieren einer kleinen Anzahl großer Blöcke (aus verschiedenen Gründen) effizienter ist als das Kopieren einer großen Anzahl kleiner Blöcke. Ich vermeide möglicherweise eine zusätzliche Zuordnung der Eingabezeichenfolge über die naive Methode.
Daniel
2
Es war ein Kommentar, der besagt, dass ich Ihrer Behauptung nicht glaube, dass Ihre Lösung in Bezug auf Effizienz viel besser ist als die naive Lösung. Bei meinen Messungen ist Ihr Code im Vergleich zur naiven Lösung schneller mit kleinen Zeichenfolgen und vielen Wiederholungen, aber langsamer mit langen Zeichenfolgen und wenigen Wiederholungen. Können Sie Links bereitstellen, in denen die verschiedenen Gründe, warum das Kopieren einiger großer Blöcke eine höhere Leistung als das Kopieren vieler kleiner Blöcke aufweist, ausführlicher erläutert werden? Ich kann mir eine Verzweigungsvorhersage vorstellen. In Bezug auf den CPU-Cache bin ich mir nicht sicher, welche Variante bevorzugt wird.
Florian Kaufmann
@FlorianKaufmann Ich sehe keinen signifikanten Unterschied zwischen groß strund klein nzwischen den beiden Ansätzen. Ich glaube, dies hat mehr mit der gesamten Pipeline zu tun als mit der Vorhersage von Zweigen an sich. Es gibt auch Probleme bei der Datenausrichtung , die berücksichtigt werden müssen. Sie sollten eine neue Frage stellen, um herauszufinden, warum dies prozessor- / speicherfreundlicher ist. Ich bin sicher, dass dies großes Interesse wecken und eine bessere Antwort erhalten würde, als ich hier geben kann.
Daniel
1
@FlorianKaufmann: Auf x86 rep movsbist eine der effizientesten Möglichkeiten zum Kopieren, zumindest für mittlere bis große Kopien. Die mikrocodierte Implementierung hat einen nahezu konstanten Startaufwand (sowohl bei AMD als auch bei Intel), z. B. bei Sandybridge, ~ 15 bis 40 Zyklen plus 4 Zyklen pro 64B-Cache-Zeile (bester Fall) . Für kleine Kopien ist eine SSE-Schleife am besten geeignet, da sie keinen Startaufwand hat. Aber dann ist es abhängig von Branchenfehlvorhersagen.
Peter Cordes
5

ITNOA

Sie können dazu die C ++ - Funktion verwenden.

 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
quelle
1
Was um alles in der Welt bedeutet "ITNOA"? Ich kann online keinen Hinweis darauf finden.
Nach dem