Come ripetere una stringa un numero variabile di volte in C ++?


127

Voglio inserire 'n' spazi (o qualsiasi stringa) all'inizio di una stringa in C ++. C'è un modo diretto per farlo utilizzando std :: strings o char * strings?

Ad esempio, in Python potresti semplicemente farlo

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

Qualcuno fornisce una risposta usando QString?
Akiva

Risposte:



39

Non esiste un modo idiomatico diretto per ripetere stringhe in C ++ equivalenti all'operatore * in Python o all'operatore x in Perl. Se stai ripetendo un singolo carattere, il costruttore a due argomenti (come suggerito dalle risposte precedenti) funziona bene:

std::string(5, '.')

Questo è un esempio artificioso di come potresti usare un ostringstream per ripetere una stringa n volte:

#include <sstream>

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

A seconda dell'implementazione, questo potrebbe essere leggermente più efficiente della semplice concatenazione della stringa n volte.


17

Usa una delle forme di string :: insert:

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

Questo inserirà "....." (cinque punti) all'inizio della stringa (posizione 0).


16
L'OP ha chiesto di ripetere una stringa, non un carattere.
Brent

@Brent L'OP ha chiesto entrambi - "'n' spazi (o qualsiasi stringa)", e poi continua a dimostrare il loro intento con un singolo punto come stringa. L'inglese non è la prima lingua di molte persone, quindi a volte è necessario indovinare i loro requisiti esatti e quando analizzato, la domanda è davvero come farlo con un singolo carattere. Mi dispiace che tu abbia trovato la mia risposta abbastanza inutile da doverla votare per difetto.
camh

13

So che questa è una vecchia domanda, ma stavo cercando di fare la stessa cosa e ho trovato quella che penso sia una soluzione più semplice. Sembra che cout abbia questa funzione incorporata in cout.fill (), vedere il collegamento per una spiegazione "completa"

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

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

uscite

.....lolcat

6
solo punti: cambia l'ultima riga in ...cout << "" << endl;
musefan

9

Ai fini dell'esempio fornito dal PO std :: ctor della stringa è sufficiente: std::string(5, '.'). Tuttavia, se qualcuno sta cercando una funzione per ripetere std :: string più volte:

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

8

Come alludeva il Commodoro Jaeger, non credo che nessuna delle altre risposte risponda effettivamente a questa domanda; la domanda chiede come ripetere una stringa, non un carattere.

Sebbene la risposta data da Commodore sia corretta, è piuttosto inefficiente. Ecco un'implementazione più veloce, l'idea è di ridurre al minimo le operazioni di copia e le allocazioni di memoria aumentando prima in modo esponenziale la stringa:

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

Possiamo anche definire un operator*per ottenere qualcosa di più vicino alla versione Python:

#include <utility>

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

Sulla mia macchina questo è circa 10 volte più veloce dell'implementazione fornita da Commodore, e circa 2 volte più veloce di una soluzione ingenua "aggiungi n - 1 volte" .


La tua implementazione non "riduce al minimo la copia". Ricorda che +=all'interno del tuo ciclo for internamente ha anche un ciclo di qualche tipo che fa str.size()iterazioni. str.size()cresce in ogni iterazione del ciclo esterno, quindi dopo ogni iterazione esterna il ciclo interno deve eseguire più iterazioni. La tua implementazione e l'ingenua "copia n volte" in totale copiano entrambi i n * periodcaratteri. La tua implementazione esegue solo un'allocazione di memoria a causa dell'iniziale reserve. Immagino che tu abbia profilato la tua implementazione con un piccolo stre un grande n, ma non anche con un grande stre un piccolo n.
Florian Kaufmann

@FlorianKaufmann Non sono sicuro del motivo per cui hai scelto di attaccare la mia risposta. Ma per "ridurre al minimo la copia" intendo "operazioni di copia". L'idea è che copiare un piccolo numero di blocchi grandi è più efficiente (per una serie di motivi) che copiare un numero elevato di blocchi piccoli. Potenzialmente evito un'allocazione aggiuntiva sulla stringa di input rispetto al metodo ingenuo.
Daniel

2
Era un commento in cui affermava che non credo alla tua affermazione che la tua soluzione sia molto migliore in termini di efficienza rispetto alla soluzione ingenua. Nelle mie misurazioni, rispetto alla soluzione ingenua, il tuo codice è più veloce con stringhe piccole e molte ripetizioni, ma più lento con stringhe lunghe e poche ripetizioni. Potreste fornire collegamenti che spieghino in modo più dettagliato la varietà di motivi per cui la copia di pochi grandi blocchi ha prestazioni superiori rispetto alla copia di molti piccoli blocchi? Mi viene in mente la previsione dei rami. Per quanto riguarda la cache della CPU, non sono sicuro di quale variante sia preferita.
Florian Kaufmann

@FlorianKaufmann Non vedo differenze significative per grandi stre piccoli ntra i due approcci. Credo che questo abbia più a che fare con la pipeline complessiva che con la previsione dei rami di per sé, ci sono anche problemi di allineamento dei dati da considerare. Dovresti porre una nuova domanda per i dettagli del motivo per cui è più compatibile con il processore / la memoria, sono sicuro che guadagnerebbe molto interesse e riceverebbe una risposta migliore di quella che posso dare qui.
Daniel

1
@FlorianKaufmann: su x86, rep movsbè uno dei modi più efficienti per copiare, almeno per copie medio-grandi. La sua implementazione microcodificata ha un overhead di avvio quasi costante (sia su AMD che Intel), ad esempio su Sandybridge, da ~ 15 a 40 cicli, più 4 cicli per linea di cache da 64B (caso migliore) . Per le piccole copie, un ciclo SSE è il migliore perché non ha il sovraccarico di avvio. Ma poi è soggetto a errori di previsione del ramo.
Peter Cordes


5

ITNOA

Puoi usare la funzione C ++ per farlo.

 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
Cosa diavolo significa "ITNOA"? Non riesco a trovare alcun riferimento ad esso online.
Dopo il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.