Existe-t-il un moyen simple de renvoyer une chaîne répétée X fois?

318

J'essaie d'insérer un certain nombre d'indentations avant une chaîne en fonction de la profondeur des éléments et je me demande s'il existe un moyen de renvoyer une chaîne répétée X fois. Exemple:

string indent = "---";
Console.WriteLine(indent.Repeat(0)); //would print nothing.
Console.WriteLine(indent.Repeat(1)); //would print "---".
Console.WriteLine(indent.Repeat(2)); //would print "------".
Console.WriteLine(indent.Repeat(3)); //would print "---------".
Abe Miessler
la source
9
Je ne sais pas si cela est applicable, mais vous voudrez peut-être également consulter le IndentedTextWriter .
Chris Shouts
Voulez-vous envisager de modifier la réponse acceptée car votre question concerne la répétition des chaînes et non les caractères que la réponse acceptée représente? À partir d'aujourd'hui jusqu'à .Net v4.7, tout constructeur de Stringclasse surchargé ne prend pas de chaîne comme paramètre pour en créer des répétitions, ce qui aurait pu faire de la réponse acceptée un ajustement parfait.
RBT

Réponses:

538

Si vous ne souhaitez répéter que le même caractère, vous pouvez utiliser le constructeur de chaîne qui accepte un caractère et le nombre de fois pour le répéter new String(char c, int count).

Par exemple, pour répéter un tiret cinq fois:

string result = new String('-', 5);
Output: -----
Ahmad Mageed
la source
8
Merci de m'avoir rappelé cette jolie petite fonctionnalité. Je pense que c'était perdu dans le coin de mon esprit.
Chris Shouts
3
Étant donné que le métacaractère tab «\ t» est un caractère, cela fonctionne également pour l'indentation.
cori
91
C'est une astuce très utile de C #, mais le titre de la question pose sur une chaîne (pas un caractère). Les autres réponses ci-dessous répondent en fait à la question, mais sont bien moins bien notées. Je n'essaie pas de manquer de respect à la réponse d'Ahmad, mais je pense que le titre de cette question devrait être changé (si la question concerne réellement les personnages) ou les autres réponses devraient vraiment être surévaluées (et pas celle-ci).
pghprogrammer4
14
@Ahmad Comme je l'ai dit, je n'ai pas déçu parce que je ne pense pas que vous ayez fourni de bonnes informations, mais parce que les informations que vous avez fournies ne sont pas pertinentes pour répondre à la question comme indiqué actuellement . Imaginez si vous cherchiez un moyen de répéter des chaînes (pas des caractères) et lorsque vous arrivez à la question pertinente, vous devez faire défiler plusieurs conseils `` utiles '' pour obtenir les réponses réelles à la question. Si le downvote vous offense, je peux le supprimer. Mais voyez-vous d'où je viens? Suis-je totalement hors de la base ici?
pghprogrammer4
11
@ pghprogrammer4 À mon humble avis, les downvotes peuvent être assez décourageants et ne devraient être utilisés que pour les réponses nuisibles / trompeuses / really_bad, qui nécessitent un changement majeur ou devraient être supprimées par leur auteur. Les réponses existent parce que les gens sont généreux et souhaitent partager ce qu'ils savent. Généralement, ces sites encouragent vraiment les votes positifs pour apporter des réponses utiles au sommet.
ToolmakerSteve
271

Si vous utilisez .NET 4.0, vous pouvez l'utiliser string.Concatavec Enumerable.Repeat.

int N = 5; // or whatever
Console.WriteLine(string.Concat(Enumerable.Repeat(indent, N)));

Sinon, j'irais avec quelque chose comme la réponse d'Adam .

La raison pour laquelle je ne conseillerais généralement pas d' utiliser la réponse d'Andrey est simplement que l' ToArray()appel introduit des frais généraux superflus qui sont évités avec l' StringBuilderapproche suggérée par Adam. Cela dit, au moins cela fonctionne sans nécessiter .NET 4.0; et c'est rapide et facile (et ne va pas vous tuer si l'efficacité n'est pas trop une préoccupation).

Dan Tao
la source
3
Cela fonctionnera parfaitement avec les chaînes - comme si vous devez concaténer des espaces insécables (& nbsp;). Mais je suppose que le constructeur de chaînes sera le plus rapide si vous avez affaire à un caractère ...
woodbase
1
Le constructeur de chaîne ne fonctionnera que pour un seul caractère. à l'option d'utiliser Concat Repeat fonctionnera pour les chaînes
Eli Dagan
4
Merci d'avoir répondu à la question du titre!
Mark K Cowan
J'aime cette réponse!
Ji_in_coding
Si vous obtenez -System.Linq.Enumerable etc. en sortie, c'est parce que vous (ok, c'était moi) avez été trop rapide pour copier / coller et modifier l'extrait de code. Si vous avez besoin de concaténer une chaîne différente sur la sortie de Enumerable.Repeat, vous devez le faire comme ceci: Console.WriteLine(string.Concat("-", string.Concat(Enumerable.Repeat(indent, N))));
Gabe
47
public static class StringExtensions
{
    public static string Repeat(this string input, int count)
    {
        if (!string.IsNullOrEmpty(input))
        {
            StringBuilder builder = new StringBuilder(input.Length * count);

            for(int i = 0; i < count; i++) builder.Append(input);

            return builder.ToString();
        }

        return string.Empty;
    }
}
Adam Robinson
la source
6
Autant passer input.Length * countau StringBuilderconstructeur, vous ne trouvez pas?
Dan Tao
Et si vous passez input.Length * countau StringBuilderconstructeur, vous pouvez ignorer le StringBuildertout et créer immédiatement un caractère [] de la bonne taille.
dtb
@dtb: Je suis curieux de savoir pourquoi vous voudriez faire ça. Je ne peux pas imaginer que ce serait beaucoup (le cas échéant) plus rapide que d'utiliser un StringBuilder, et le code serait moins transparent.
Adam Robinson
@dtb: beurk. Utilisez StringBuilder; il a été conçu pour le faire efficacement.
ToolmakerSteve
Adam, je vois que vous vous êtes donné la peine de tester l'entrée pour null lorsque vous l'ajoutez à count, mais que vous avez ensuite laissé passer cela, en vous appuyant sur Append pour ne rien faire étant donné la valeur null. À mon humble avis, cela n'est pas évident: si null est une entrée possible, pourquoi ne pas tester cela à l'avance et renvoyer une chaîne vide?
ToolmakerSteve
46

solution la plus performante pour la chaîne

string result = new StringBuilder().Insert(0, "---", 5).ToString();
c0rd
la source
1
Bien sûr, c'est une jolie doublure. Certainement pas le plus performant. StringBuilder a un peu de surcharge sur un très petit nombre de concaténations.
Teejay
25

Pour de nombreux scénarios, c'est probablement la solution la plus intéressante:

public static class StringExtensions
{
    public static string Repeat(this string s, int n)
        => new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
}

L'utilisation est alors:

text = "Hello World! ".Repeat(5);

Cela s'appuie sur d'autres réponses (en particulier @ c0rd's). En plus de la simplicité, il présente les caractéristiques suivantes, que toutes les autres techniques discutées ne partagent pas:

  • Répétition d'une chaîne de n'importe quelle longueur, pas seulement d'un caractère (comme demandé par l'OP).
  • Utilisation efficace de StringBuilderla préallocation via le stockage.
Bob Sammers
la source
Je suis d'accord, c'est merveilleux. Je viens d'ajouter cela à mon code avec crédit à Bob! Merci!
John Burley
22

Utilisez String.PadLeft , si la chaîne souhaitée ne contient qu'un seul caractère.

public static string Indent(int count, char pad)
{
    return String.Empty.PadLeft(count, pad);
}

Crédit dû ici

Steve Townsend
la source
1
BAISER. Pourquoi avons-nous besoin de StringBuilders et d'extensions? Il s'agit d'un problème très simple.
DOK
1
Je sais pas, cela semble résoudre le problème tel que formulé avec une certaine élégance. Et btw, qui appelez-vous «S»? :-)
Steve Townsend
7
Mais est - ce pas vraiment juste une version moins évidente de la stringconstructeur qui prend un charet unint ?
Dan Tao
Ce serait plus joli avec String.Empty au lieu de "" ... sinon, bonne solution;)
Thomas Levesque
@Steve BTW les String.PadLeftarguments sont inversés. Le countvient avant le caractère de remplissage.
Ahmad Mageed
17

Vous pouvez répéter votre chaîne (au cas où ce ne serait pas un seul caractère) et concaténer le résultat, comme ceci:

String.Concat(Enumerable.Repeat("---", 5))
LiRoN
la source
12

J'irais pour la réponse de Dan Tao, mais si vous n'utilisez pas .NET 4.0, vous pouvez faire quelque chose comme ça:

public static string Repeat(this string str, int count)
{
    return Enumerable.Repeat(str, count)
                     .Aggregate(
                        new StringBuilder(),
                        (sb, s) => sb.Append(s))
                     .ToString();
}
Thomas Levesque
la source
3
Si je trouve cet extrait dans mon projet, je me demanderai qui a violé le KISS et pourquoi ... bien sinon
Noctis
4
        string indent = "---";
        string n = string.Concat(Enumerable.Repeat(indent, 1).ToArray());
        string n = string.Concat(Enumerable.Repeat(indent, 2).ToArray());
        string n = string.Concat(Enumerable.Repeat(indent, 3).ToArray());
Andrey
la source
1
@Adam: Je suppose que vous auriez besoin de faire cela avant .NET 4.0.
Dan Tao
3

J'aime la réponse donnée. Dans le même esprit, c'est ce que j'ai utilisé dans le passé:

"" .PadLeft (3 * tiret, '-')

Cela remplira la création d'un retrait, mais techniquement, la question était de répéter une chaîne. Si le retrait de la chaîne est quelque chose comme> - <alors cela ainsi que la réponse acceptée ne fonctionneraient pas. Dans ce cas, la solution de c0rd utilisant stringbuilder semble bonne, bien que la surcharge de stringbuilder puisse en fait ne pas en faire la plus performante. Une option consiste à créer un tableau de chaînes, à le remplir de chaînes de retrait, puis à le concaténer. Pour:

int Indent = 2;

string[] sarray = new string[6];  //assuming max of 6 levels of indent, 0 based

for (int iter = 0; iter < 6; iter++)
{
    //using c0rd's stringbuilder concept, insert ABC as the indent characters to demonstrate any string can be used
    sarray[iter] = new StringBuilder().Insert(0, "ABC", iter).ToString();
}

Console.WriteLine(sarray[Indent] +"blah");  //now pretend to output some indented line

Nous aimons tous une solution intelligente, mais parfois simple est préférable.

Londo Mollari
la source
3

Ajout de la méthode d'extension que j'utilise partout dans mes projets:

public static string Repeat(this string text, int count)
{
    if (!String.IsNullOrEmpty(text))
    {
        return String.Concat(Enumerable.Repeat(text, count));
    }
    return "";
}

J'espère que quelqu'un pourra en profiter ...

Mayer Spitzer
la source
2

Surpris, personne n'est allé à la vieille école. Je ne fais aucune réclamation sur ce code, mais juste pour le plaisir:

public static string Repeat(this string @this, int count)
{
    var dest = new char[@this.Length * count];
    for (int i = 0; i < dest.Length; i += 1)
    {
        dest[i] = @this[i % @this.Length];
    }
    return new string(dest);
}
OlduwanSteve
la source
Évidemment, je l'ai laissé comme test pour le lecteur astucieux :) Très astucieux, merci.
OlduwanSteve
1
Nommer un paramètre @this n'a rien de démodé, cela a toujours été une horrible idée :)
Dave Jellison
2
Je préfère ne pas utiliser de mot-clé comme nom de variable à moins qu'il ne soit forcé (comme @class = "quelque chose" lorsque vous travaillez avec MVC parce que vous avez besoin d'une classe faisant référence à CSS mais c'est un mot-clé en C # (bien sûr). Ce n'est pas seulement moi qui parle, c'est une règle générale. Bien qu'il s'agisse certainement de code compilable et légitime, pensez à la facilité de lecture et de frappe également, deux raisons pratiques. int count) pourrait être plus illustratif à mon humble avis
Dave Jellison
1
Intéressant, merci. Je pense que dans ce cas, vous avez probablement raison de dire que @this est moins descriptif qu'il ne pourrait l'être. Cependant, en général, je pense qu'il est assez courant d'étendre des classes ou des interfaces où le seul nom raisonnable pour «ceci» est plutôt générique. Si vous avez un paramètre appelé «instance» ou «it» ou «str» (voir MSDN ), alors vous vouliez probablement dire «ceci».
OlduwanSteve
1
Je suis fondamentalement d'accord avec vous, mais pour les besoins de l'argument ... puisque j'en sais plus sur la chaîne que je construis, je peux faire un travail légèrement meilleur que StringBuilder. StringBuilder doit faire une supposition lorsqu'il alloue de la mémoire. Certes, il est peu probable que cela ait de l'importance dans de nombreux scénarios, mais quelqu'un pourrait construire un nombre énorme de chaînes répétées en parallèle et être heureux de gagner quelques cycles d'horloge en arrière :)
OlduwanSteve
2

Je ne sais pas comment cela fonctionnerait, mais c'est un morceau de code facile. (Je l'ai probablement rendu plus compliqué qu'il ne l'est.)

int indentCount = 3;
string indent = "---";
string stringToBeIndented = "Blah";
// Need dummy char NOT in stringToBeIndented - vertical tab, anyone?
char dummy = '\v';
stringToBeIndented.PadLeft(stringToBeIndented.Length + indentCount, dummy).Replace(dummy.ToString(), indent);

Alternativement, si vous connaissez le nombre maximum de niveaux auxquels vous pouvez vous attendre, vous pouvez simplement déclarer un tableau et l'indexer. Vous voudrez probablement rendre ce tableau statique ou constant.

string[] indents = new string[4] { "", indent, indent.Replace("-", "--"), indent.Replace("-", "---"), indent.Replace("-", "----") };
output = indents[indentCount] + stringToBeIndented;      
BH
la source
2

Je n'ai pas assez de représentant pour commenter la réponse d'Adam, mais la meilleure façon de le faire imo est comme ceci:

public static string RepeatString(string content, int numTimes) {
        if(!string.IsNullOrEmpty(content) && numTimes > 0) {
            StringBuilder builder = new StringBuilder(content.Length * numTimes);

            for(int i = 0; i < numTimes; i++) builder.Append(content);

            return builder.ToString();
        }

        return string.Empty;
    }

Vous devez vérifier pour voir si numTimes est supérieur à zéro, sinon vous obtiendrez une exception.

Gru
la source
2

Je n'ai pas vu cette solution. Je trouve cela plus simple pour ma situation actuelle en développement logiciel:

public static void PrintFigure(int shapeSize)
{
    string figure = "\\/";
    for (int loopTwo = 1; loopTwo <= shapeSize - 1; loopTwo++)
    {
        Console.Write($"{figure}");
    }
}
Nelly
la source
2

Pour une utilisation générale, les solutions impliquant la classe StringBuilder sont les meilleures pour répéter des chaînes à plusieurs caractères. Il est optimisé pour gérer la combinaison d'un grand nombre de chaînes d'une manière que la concaténation simple ne peut pas et qui serait difficile ou impossible à faire plus efficacement à la main. Les solutions StringBuilder présentées ici utilisent O (N) itérations pour terminer, un taux forfaitaire proportionnel au nombre de répétitions.

Cependant, pour un très grand nombre de répétitions, ou lorsque des niveaux élevés d'efficacité doivent être éliminés, une meilleure approche consiste à faire quelque chose de similaire aux fonctionnalités de base de StringBuilder mais à produire des copies supplémentaires à partir de la destination, plutôt qu'à partir de la chaîne d'origine, comme ci-dessous.

    public static string Repeat_CharArray_LogN(this string str, int times)
    {
        int limit = (int)Math.Log(times, 2);
        char[] buffer = new char[str.Length * times];
        int width = str.Length;
        Array.Copy(str.ToCharArray(), buffer, width);

        for (int index = 0; index < limit; index++)
        {
            Array.Copy(buffer, 0, buffer, width, width);
            width *= 2;
        }
        Array.Copy(buffer, 0, buffer, width, str.Length * times - width);

        return new string(buffer);
    }

Cela double la longueur de la chaîne source / destination à chaque itération, ce qui évite la réinitialisation des compteurs à chaque fois qu'elle parcourt la chaîne d'origine, au lieu de cela de lire et de copier en douceur la chaîne désormais beaucoup plus longue, ce que les processeurs modernes peuvent faire beaucoup. plus efficacement.

Il utilise un logarithme en base 2 pour trouver le nombre de fois qu'il a besoin de doubler la longueur de la chaîne, puis continue de le faire autant de fois. Étant donné que le reste à copier est désormais inférieur à la longueur totale à partir de laquelle il copie, il peut alors simplement copier un sous-ensemble de ce qu'il a déjà généré.

J'ai utilisé la méthode Array.Copy () sur l'utilisation de StringBuilder, car une copie du contenu de StringBuilder en lui-même aurait pour surcharge de produire une nouvelle chaîne avec ce contenu à chaque itération. Array.Copy () évite cela, tout en fonctionnant avec un taux d'efficacité extrêmement élevé.

Cette solution prend O (1 + log N) pour terminer, un taux qui augmente logarithmiquement avec le nombre de répétitions (doubler le nombre de répétitions équivaut à une itération supplémentaire), une économie substantielle par rapport aux autres méthodes, qui augmentent proportionnellement.

buchWyrm
la source
1

Une autre approche consiste à considérer stringcomme IEnumerable<char>et ont une méthode d'extension générique qui multipliera les éléments d'une collection par le facteur spécifié.

public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source, int times)
{
    source = source.ToArray();
    return Enumerable.Range(0, times).SelectMany(_ => source);
}

Donc dans votre cas:

string indent = "---";
var f = string.Concat(indent.Repeat(0)); //.NET 4 required
//or
var g = new string(indent.Repeat(5).ToArray());
nawfal
la source
1

Imprimez une ligne avec répétition.

Console.Write(new string('=', 30) + "\n");

==============================

Kamal
la source
Pourquoi n'avez-vous pas utilisé Console.WriteLine au lieu d'ajouter \ n à la fin de la chaîne?
InxaneNinja
-1

Vous pouvez créer une ExtensionMethod pour ce faire!

public static class StringExtension
{
  public static string Repeat(this string str, int count)
  {
    string ret = "";

    for (var x = 0; x < count; x++)
    {
      ret += str;
    }

    return ret;
  }
}

Ou en utilisant la solution @Dan Tao:

public static class StringExtension
{
  public static string Repeat(this string str, int count)
  {
    if (count == 0)
      return "";

    return string.Concat(Enumerable.Repeat(indent, N))
  }
}
Zote
la source
3
Voir mon commentaire à la réponse de Kyle
Thomas Levesque