Puis-je «multiplier» une chaîne (en C #)?

136

Supposons que j'ai une chaîne, par exemple,

string snip =  "</li></ul>";

Je veux essentiellement l'écrire plusieurs fois, en fonction d'une valeur entière.

string snip =  "</li></ul>";
int multiplier = 2;

// TODO: magic code to do this 
// snip * multiplier = "</li></ul></li></ul>";

EDIT: Je sais que je peux facilement écrire ma propre fonction pour implémenter cela, je me demandais juste s'il y avait un opérateur de chaîne étrange que je ne connaissais pas

en dépit
la source
Double-possible de Y a
Michael Freidgeim

Réponses:

222

Dans .NET 4, vous pouvez faire ceci:

String.Concat(Enumerable.Repeat("Hello", 4))
Will Dean
la source
9
Et ce n'est pas beaucoup plus dans .NET 3.5: String.Concat (Enumerable.Repeat ("Hello", 4) .ToArray ())
Mark Foreman
4
Je ne pense pas du tout que ce soit élégant. En python, le code pour faire cela est: snip * multiplier (Ce n'est pas horrible ... mais ce n'est pas beau non plus).
hérisson dément le
7
@dementedhedgehog Je sais que vous reconnaissez votre propre démence, mais même si vous souffrez comme vous, vous pourrez peut-être voir qu'un exemple de Python n'aurait pas fait une très bonne réponse à une question C # ... Je pense que la plupart d'entre nous peuvent voir que le choix de la langue est toujours un compromis, et quasiment toute réponse à n'importe quelle question peut être accompagnée d'un plaidoyer, pour ou contre.
Will Dean
1
@will dean: Je faisais simplement référence à la déclaration de Matthew Nichols sur l'élégance du code que vous avez là-bas. Pour autant que je sache, votre code est élégant pour C #, tel qu'il est actuellement, pour ce problème. Le point que j'essaie de faire est: (je crois) qu'il serait très facile (pour Microsoft, car les chaînes sont scellées) d'étendre C # pour surcharger l'opérateur * pour permettre la multiplication de chaînes int *. Ce qui serait alors en fait élégant dans un sens universel, à mon humble avis, pas seulement élégant dans le contexte des contraintes imposées par C # tel qu'il existe actuellement.
hérisson
@dementedhedgehog Pour moi, cela va à l'encontre de ce que operator*représente le binaire . À chacun le leur, je suppose.
TEK
99

Notez que si votre "chaîne" n'est qu'un seul caractère, il y a une surcharge du constructeur de chaîne pour la gérer:

int multipler = 10;
string TenAs = new string ('A', multipler);
James Curran
la source
C'est vraiment pro.
Zaven Zareyan
61

Malheureusement / heureusement, la classe string est scellée, vous ne pouvez donc pas en hériter et surcharger l'opérateur *. Vous pouvez cependant créer une méthode d'extension:

public static string Multiply(this string source, int multiplier)
{
   StringBuilder sb = new StringBuilder(multiplier * source.Length);
   for (int i = 0; i < multiplier; i++)
   {
       sb.Append(source);
   }

   return sb.ToString();
}

string s = "</li></ul>".Multiply(10);
Tamas Czinege
la source
5
Juste là où j'allais! Vous pourriez probablement optimiser en utilisant le multiplicateur source.Length * dans le ctor StringBuilder
Marc Gravell
2
Le commentaire de Marc était juste où j'allais :)
Jon Skeet
1
Vous avez besoin de (source.Length * multiplicateur), pas seulement (multiplicateur)
Marc Gravell
1
Très sûr. Il alloue une chaîne (de cette longueur) dans les coulisses, puis la mute. Cela ne fonctionne pas comme un List <T> normal etc.
Marc Gravell
1
La méthode d'extension est idéale ici.
Chris Ballance
12

Je suis avec DrJokepu sur celui-ci , mais si, pour une raison quelconque, vous voulez tricher en utilisant la fonctionnalité intégrée, vous pouvez faire quelque chose comme ceci:

string snip = "</li></ul>";
int multiplier = 2;

string result = string.Join(snip, new string[multiplier + 1]);

Ou, si vous utilisez .NET 4:

string result = string.Concat(Enumerable.Repeat(snip, multiplier));

Personnellement, je ne dérangerais pas - une méthode d'extension personnalisée est beaucoup plus agréable.

LukeH
la source
1
Je n'ai jamais su qu'il y avait une triche comme celle-là. =)
Jronny
10

Juste pour être complet - voici une autre façon de procéder:

public static string Repeat(this string s, int count)
{
    var _s = new System.Text.StringBuilder().Insert(0, s, count).ToString();
    return _s;
}

Je pense que j'ai tiré celui-là de Stack Overflow il y a quelque temps, donc ce n'est pas mon idée.

user51710
la source
9

Vous devrez écrire une méthode - bien sûr, avec C # 3.0, cela pourrait être une méthode d'extension:

public static string Repeat(this string, int count) {
    /* StringBuilder etc */ }

puis:

string bar = "abc";
string foo = bar.Repeat(2);
Marc Gravell
la source
Est-ce que même .NET3 n'avait pas Enumerable.Repeat?
Will Dean
@ Will - .NET 3 était WCF / WPF etc., donc non; il existe dans .NET 3.5, mais vous en auriez besoin string.Joinégalement - pourquoi ne pas faire une boucle n fois? Beaucoup plus direct.
Marc Gravell
Merci - Je ne pensais pas correctement à 3,0 contre 3,5. Quant à savoir pourquoi ne pas simplement utiliser une boucle, c'est sûrement l'essence même du débat fonctionnel vs impératif? J'ai posté une réponse .net 4 plus bas, ce qui, à mon avis, n'est pas si mal dans le débat sur «l'intelligence fonctionnelle» et «l'évidence en boucle».
Will Dean
8

Un peu tard (et juste pour le plaisir), si vous voulez vraiment utiliser l' *opérateur pour ce travail, vous pouvez le faire:

public class StringWrap
{
    private string value;
    public StringWrap(string v)
    {
        this.value = v;
    }
    public static string operator *(StringWrap s, int n)
    {
        return s.value.Multiply(n); // DrJokepu extension
    }
}

Et donc:

var newStr = new StringWrap("TO_REPEAT") * 5;

Notez que, aussi longtemps que vous êtes en mesure de trouver un comportement raisonnable pour eux, vous pouvez également gérer d' autres opérateurs par StringWrapclasse, comme \, ^, %etc ...

PS:

Multiply()crédits d'extension à @DrJokepu tous droits réservés ;-)

digEmAll
la source
7

C'est beaucoup plus concis:

new StringBuilder().Insert(0, "</li></ul>", count).ToString()

L'espace de noms using System.Text;doit être importé dans ce cas.

utilisateur734119
la source
2
string Multiply(string input, int times)
{
     StringBuilder sb = new StringBuilder(input.length * times);
     for (int i = 0; i < times; i++)
     {
          sb.Append(input);
     }
     return sb.ToString();
}
Chris Ballance
la source
2

Si vous avez .Net 3.5 mais pas 4.0, vous pouvez utiliser System.Linq's

String.Concat(Enumerable.Range(0, 4).Select(_ => "Hello").ToArray())
Frank Schwieterman
la source
Pour obtenir une seule chaîne dont vous avez encore besoin String.Concat, et sur 3.5, vous en aurez également besoin .ToArray(). Pas la solution la plus élégante, j'en ai peur.
Kobi
2

Puisque tout le monde ajoute ses propres exemples .NET4 / Linq, je pourrais aussi bien ajouter les miens. (Fondamentalement, c'est DrJokepu, réduit à une seule ligne)

public static string Multiply(this string source, int multiplier) 
{ 
    return Enumerable.Range(1,multiplier)
             .Aggregate(new StringBuilder(multiplier*source.Length), 
                   (sb, n)=>sb.Append(source))
             .ToString();
}
James Curran
la source
0

D'accord, voici mon avis sur la question:

public static class ExtensionMethods {
  public static string Multiply(this string text, int count)
  {
    return new string(Enumerable.Repeat(text, count)
      .SelectMany(s => s.ToCharArray()).ToArray());
  }
}

Je suis un peu ridicule bien sûr, mais quand j'ai besoin d'avoir une tabulation dans des classes générant du code, Enumerable.Repeat le fait pour moi. Et oui, la version StringBuilder est bien aussi.

Dmitri Nesteruk
la source
0

Voici mon avis à ce sujet juste pour référence future:

    /// <summary>
    /// Repeats a System.String instance by the number of times specified;
    /// Each copy of thisString is separated by a separator
    /// </summary>
    /// <param name="thisString">
    /// The current string to be repeated
    /// </param>
    /// <param name="separator">
    /// Separator in between copies of thisString
    /// </param>
    /// <param name="repeatTimes">
    /// The number of times thisString is repeated</param>
    /// <returns>
    /// A repeated copy of thisString by repeatTimes times 
    /// and separated by the separator
    /// </returns>
    public static string Repeat(this string thisString, string separator, int repeatTimes) {
        return string.Join(separator, ParallelEnumerable.Repeat(thisString, repeatTimes));
    }
Jronny
la source
J'espère que vous ne l'utilisez pas vraiment ParallelEnumerabledans des situations comme celle-ci. string.Joina besoin d'utiliser les éléments dans l'ordre, donc la mise en parallèle de leur génération est inutile.
Gabe
@Gabe: Puisque les articles sont les mêmes et ne sont en réalité que des copies de thisString, il n'y a pas besoin de s'inquiéter de la commande ici, je suppose.
Jronny
Je suis d'accord avec beaucoup d'enseignants dans le domaine qu'il est généralement bon de coder pour dire ce que vous voulez dire. Il n'y a aucun avantage à dire que vous voulez ce parallèle et ne pense en secret « Eh bien, je sais que ce ne sera pas parallèle dans ce cas particulier de toute façon »
sehe
Je pense que le rendre parallèle le rend plus rapide, ou s'il vous plaît, éclairez-moi. C'est pourquoi je convertis cela en ParallelEnumerable, donc je pense que je code pour dire ce que je veux dire ... Merci.
Jronny