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


Double-possible de Y a
Michael Freidgeim

Réponses:


222

Dans .NET 4, vous pouvez faire ceci:

String.Concat(Enumerable.Repeat("Hello", 4))

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);

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);

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.


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.


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);

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 ;-)


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.


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();
}

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())

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();
}

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.


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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.