Comment répéter une chaîne un nombre variable de fois en C ++?

127

Je veux insérer des espaces 'n' (ou n'importe quelle chaîne) au début d'une chaîne en C ++. Existe-t-il un moyen direct de le faire en utilisant soit std :: strings ou char * strings?

Par exemple, en Python, vous pouvez simplement faire

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

la source
Quelqu'un a fourni une réponse en utilisant QString?
Akiva

Réponses:

175

Dans le cas particulier de la répétition d'un seul caractère, vous pouvez utiliser std::string(size_type count, CharT ch):

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

NB. Cela ne peut pas être utilisé pour répéter des chaînes à plusieurs caractères.

Luke
la source
80
L'OP a demandé de répéter une chaîne, pas un caractère.
Florian Kaufmann
39

Il n'y a pas de moyen idiomatique direct pour répéter des chaînes en C ++ équivalent à l' opérateur * en Python ou à l' opérateur x en Perl. Si vous répétez un seul caractère, le constructeur à deux arguments (comme suggéré par les réponses précédentes) fonctionne bien:

std::string(5, '.')

Ceci est un exemple artificiel de la façon dont vous pouvez utiliser un ostringstream pour répéter une chaîne n fois:

#include <sstream>

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

Selon l'implémentation, cela peut être légèrement plus efficace que de simplement concaténer la chaîne n fois.

Commodore Jaeger
la source
17

Utilisez l'une des formes de string :: insert:

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

Cela insérera "....." (cinq points) au début de la chaîne (position 0).

camh
la source
16
L'OP a demandé de répéter une chaîne, pas un caractère.
Brent
@Brent L'OP a demandé les deux - "'n' espaces (ou n'importe quelle chaîne)", puis continue à démontrer leur intention avec un seul point comme chaîne. L'anglais n'est pas la langue maternelle de beaucoup de gens, vous devez donc parfois deviner leurs exigences exactes et une fois analysé, la question est vraiment de savoir comment faire cela avec un seul caractère. Je suis désolé que vous ayez trouvé ma réponse suffisamment inutile pour que vous deviez voter contre.
camh
13

Je sais que c'est une vieille question, mais je cherchais à faire la même chose et j'ai trouvé ce que je pense être une solution plus simple. Il semble que cout ait cette fonction intégrée avec cout.fill (), voir le lien pour une explication `` complète ''

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

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

les sorties

.....lolcat
Ian
la source
6
only dots: changer la dernière ligne en ...cout << "" << endl;
musefan
9

Aux fins de l'exemple fourni par l'OP std :: La cteur de chaîne est suffisante: std::string(5, '.'). Cependant, si quelqu'un cherche une fonction pour répéter std :: string plusieurs fois:

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
la source
8

Comme le commodore Jaeger y a fait allusion, je ne pense pas qu'aucune des autres réponses ne réponde réellement à cette question; la question demande comment répéter une chaîne, pas un caractère.

Bien que la réponse donnée par Commodore soit correcte, elle est tout à fait inefficace. Voici une implémentation plus rapide, l'idée est de minimiser les opérations de copie et les allocations de mémoire en augmentant d'abord exponentiellement la chaîne:

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

Nous pouvons également définir un operator*pour se rapprocher de la version Python:

#include <utility>

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

Sur ma machine, c'est environ 10 fois plus rapide que l'implémentation donnée par Commodore, et environ 2 fois plus rapide qu'une solution naïve «ajouter n - 1 fois» .

Daniel
la source
Votre implémentation ne «minimise pas la copie». Gardez à l'esprit que l' +=intérieur de votre boucle for a également en interne une boucle qui effectue des str.size()itérations. str.size()croît à chaque itération de boucle externe, donc après chaque itération externe, la boucle interne doit faire plus d'itérations. Votre et l'implémentation naïve «copie n fois» au total les deux n * periodcaractères de copie . Votre implémentation n'effectue qu'une seule allocation de mémoire en raison de l'initial reserve. Je suppose que vous avez profilé votre implémentation avec un assez petit stret un grand n, mais pas aussi avec un grand stret un petit n.
Florian Kaufmann
@FlorianKaufmann Je ne sais pas pourquoi vous avez choisi d'attaquer ma réponse. Mais par "minimiser la copie", j'entends les "opérations de copie". L'idée étant que copier un petit nombre de gros blocs est plus efficace (pour diverses raisons) que de copier un grand nombre de petits blocs. J'évite potentiellement une allocation supplémentaire sur la chaîne d'entrée par rapport à la méthode naïve.
Daniel
2
C'était un commentaire disant que je ne crois pas que vous prétendez que votre solution est bien meilleure en termes d'efficacité que la solution naïve. Dans mes mesures, par rapport à la solution naïve, votre code est plus rapide avec de petites chaînes et de nombreuses répétitions, mais plus lent avec de longues chaînes et peu de répétitions. Pourriez-vous fournir des liens expliquant plus en détail la variété des raisons pour lesquelles la copie de quelques gros blocs a des performances plus élevées que la copie de nombreux petits blocs? Je peux penser à la prédiction de branche. En ce qui concerne le cache du processeur, je ne sais pas quelle variante est préférée.
Florian Kaufmann
@FlorianKaufmann Je ne vois aucune différence significative pour les grands stret les petits nentre les deux approches. Je pense que cela a plus à voir avec le pipeline global que la prédiction de branche en soi, il y a aussi des problèmes d'alignement des données à prendre en compte. Vous devriez poser une nouvelle question pour savoir pourquoi c'est plus convivial pour le processeur / la mémoire, je suis sûr que cela gagnerait beaucoup d'intérêt et recevrait une meilleure réponse que ce que je peux donner ici.
Daniel
1
@FlorianKaufmann: Sur x86, rep movsbest l'un des moyens les plus efficaces de copier, au moins pour les copies moyennes à grandes. Son implémentation micro-codée a une surcharge de démarrage quasi constante (sur AMD et Intel), par exemple sur Sandybridge, ~ 15 à 40 cycles, plus 4 cycles par ligne de cache 64B (meilleur cas) . Pour les petites copies, une boucle SSE est préférable car elle n'a pas de surcharge de démarrage. Mais alors, il est sujet à des erreurs de prédiction de la branche.
Peter Cordes
5

ITNOA

Vous pouvez utiliser la fonction C ++ pour ce faire.

 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
la source
1
Que diable signifie "ITNOA"? Je ne trouve aucune référence en ligne.
Après le