C'è un modo semplice per restituire una stringa ripetuta X il numero di volte?


318

Sto cercando di inserire un certo numero di rientri prima di una stringa in base alla profondità di un elemento e mi chiedo se esiste un modo per restituire una stringa ripetuta X volte. Esempio:

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 "---------".

9
Non sono sicuro che sia applicabile, ma potresti voler guardare anche IndentedTextWriter .
Chris Shouts,

Vuoi considerare di cambiare la risposta accettata poiché la tua domanda riguarda la ripetizione di stringhe e non i caratteri che la risposta accettata descrive? A partire da oggi fino a .Net v4.7, qualsiasi costruttore di Stringclasse sovraccarico non sta prendendo una stringa come parametro per creare ripetizioni da essa che avrebbero potuto rendere la risposta accettata perfetta.
RBT

Risposte:


538

Se hai intenzione di ripetere solo lo stesso carattere, puoi usare il costruttore di stringhe che accetta un carattere e il numero di volte per ripeterlo new String(char c, int count).

Ad esempio, per ripetere un trattino cinque volte:

string result = new String('-', 5);
Output: -----

8
Grazie per avermi ricordato questa simpatica piccola funzionalità. Penso che sia stato perso nell'angolo della mia mente.
Chris Shouts,

3
Poiché il tab metacharacter '\ t' è un carattere, questo funziona anche per il rientro.
cori,

91
Questo è un trucco molto utile di C #, ma il titolo della domanda è chiedere una stringa (non un carattere). Le altre risposte sotto questa stanno effettivamente rispondendo alla domanda, ma hanno un punteggio molto più basso. Non sto cercando di mancare di rispetto alla risposta di Ahmad, ma penso che il titolo di questa domanda dovrebbe essere cambiato (se la domanda riguarda effettivamente i personaggi) o che le altre risposte dovrebbero davvero essere votate (e non questa).
pghprogrammer4,

14
@Ahmad Come ho detto, non ho votato a fondo perché non penso che tu abbia fornito buone informazioni, ma perché le informazioni che hai fornito non sono rilevanti per rispondere alla domanda come attualmente indicato . Immagina se stavi cercando un modo per ripetere le stringhe (non i caratteri) e quando arrivi alla domanda pertinente devi scorrere diversi suggerimenti "utili" per arrivare alle risposte effettive alla domanda. Se il downvote ti è offensivo, posso rimuoverlo. Ma vedi da dove vengo? Sono totalmente fuori base qui?
pghprogrammer4,

11
@ pghprogrammer4 IMHO, i downgrade possono essere piuttosto scoraggianti e dovrebbero essere usati solo per risposte dannose / fuorvianti / true_bad, che necessitano di un cambiamento sostanziale o che dovrebbero essere eliminate dal loro autore. Le risposte esistono perché le persone sono generose e desiderano condividere ciò che sanno. In generale, questi siti incoraggiano davvero i voti a portare risposte utili in alto.
ToolmakerSteve

271

Se si utilizza .NET 4.0, è possibile utilizzarlo string.Concatinsieme a Enumerable.Repeat.

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

Altrimenti andrei con qualcosa come la risposta di Adam .

Il motivo per cui generalmente non consiglierei di usare la risposta di Andrey è semplicemente che la ToArray()chiamata introduce un sovraccarico superfluo che viene evitato con l' StringBuilderapproccio suggerito da Adam. Detto questo, almeno funziona senza richiedere .NET 4.0; ed è facile e veloce (e non ti ucciderà se l'efficienza non è troppo preoccupante).


3
Funzionerà perfettamente con le stringhe, ad esempio se devi concatenare spazi non interruttori (& nbsp;). Ma suppongo che il costruttore di stringhe sarà più veloce se hai a che fare con un char ...
woodbase

1
Il costruttore di stringhe funzionerà solo per un carattere. all'opzione di usare Concat Repeat funzionerà per le stringhe
Eli Dagan,

4
Grazie per aver risposto alla domanda sul titolo reale!
Mark K Cowan,

Mi piace questa risposta!
Ji_in_coding il

Se stai ottenendo -System.Linq.Enumerable ecc come output, è perché tu (ok, sono stato io) sei stato troppo veloce per copiare / incollare e modificare lo snippet. Se devi concaticare una stringa diversa sull'output dell'Enumerable.Repeat, devi farlo in questo modo: 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;
    }
}

6
Potresti anche passare input.Length * countal StringBuildercostruttore, non credi?
Dan Tao,

E se passi input.Length * countal StringBuildercostruttore, puoi saltare del StringBuildertutto e creare subito un carattere [] della giusta dimensione.
dtb,

@dtb: sono curioso di sapere perché vorresti farlo. Non riesco a immaginare che sarebbe molto più veloce dell'uso di a StringBuilder, e il codice sarebbe meno trasparente.
Adam Robinson,

@dtb: yuck. Usa StringBuilder; è stato progettato per farlo in modo efficiente.
ToolmakerSteve

Adam, vedo che sei andato al problema di testare l'input per null quando lo aggiungevi per contare, ma poi hai permesso che fallisse, facendo affidamento su Append per non fare nulla dato il null. IMHO questo è meno che ovvio: se null è un possibile input, perché non testarlo in anticipo e restituire una stringa vuota?
ToolmakerSteve

46

soluzione più performante per stringa

string result = new StringBuilder().Insert(0, "---", 5).ToString();

1
Certo è un bel one-liner. Non certo il più performante. StringBuilder ha un leggero sovraccarico su un numero molto limitato di concatenazioni.
Teejay,

25

Per molti scenari, questa è probabilmente la soluzione più accurata:

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

L'utilizzo è quindi:

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

Questo si basa su altre risposte (in particolare su @ c0rd's). Oltre alla semplicità, ha le seguenti caratteristiche, che non tutte le altre tecniche discusse condividono:

  • Ripetizione di una stringa di qualsiasi lunghezza, non solo di un carattere (come richiesto dall'OP).
  • Uso efficiente della StringBuilderpreallocazione attraverso lo storage.

Sono d'accordo, è meraviglioso. Ho appena aggiunto questo al mio codice con credito a Bob! Grazie!
John Burley,

22

Usa String.PadLeft , se la stringa desiderata contiene solo un singolo carattere.

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

Credito dovuto qui


1
BACIO. A cosa servono comunque StringBuilders ed estensioni? Questo è un problema molto semplice
DOK,

1
Non so, questo sembra risolvere il problema come espresso con un po 'di eleganza. E a proposito, chi chiami "S"? :-)
Steve Townsend,

7
Ma non è questo in realtà solo una versione meno evidente il stringcostruttore che prende una chared unaint ?
Dan Tao,

Sarebbe più bello con String.Empty invece di "" ... altrimenti, buona soluzione;)
Thomas Levesque,

@Steve BTW gli String.PadLeftargomenti sono invertiti. Il countviene prima del carattere di riempimento.
Ahmad Mageed,

17

Puoi ripetere la tua stringa (nel caso non sia un singolo carattere) e concatene il risultato, in questo modo:

String.Concat(Enumerable.Repeat("---", 5))

12

Vorrei cercare la risposta di Dan Tao, ma se non stai usando .NET 4.0 puoi fare qualcosa del genere:

public static string Repeat(this string str, int count)
{
    return Enumerable.Repeat(str, count)
                     .Aggregate(
                        new StringBuilder(),
                        (sb, s) => sb.Append(s))
                     .ToString();
}

3
Se troverò questo frammento nel mio progetto, mi chiederò chi ha violato il KISS e perché ... bello altrimenti
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());

1
@Adam: immagino che dovresti farlo prima di Internet 4.0.
Dan Tao,

3

Mi piace la risposta data. Sulla falsa riga però è quello che ho usato in passato:

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

Questo compirà la creazione di un rientro ma tecnicamente la domanda era ripetere una stringa. Se il rientro della stringa è simile a> - <, così come la risposta accettata non funzionerebbe. In questo caso la soluzione di c0rd che usa stringbuilder sembra buona, sebbene il sovraccarico di stringbuilder potrebbe in effetti non renderlo il più performante. Un'opzione è costruire una matrice di stringhe, riempirla con stringhe di rientro, quindi concatenerla. Per Pentecoste:

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

Tutti amiamo una soluzione intelligente ma a volte semplice è la cosa migliore.


3

Aggiunta del metodo di estensione che sto usando in tutti i miei progetti:

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

Spero che qualcuno possa usarlo ...


2

Nessuno sorpreso è andato alla vecchia scuola. Non sto affermando nulla su questo codice, ma solo per divertimento:

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

Ovviamente l' ho lasciato come test per il lettore astuto :) Molto astuto, grazie.
OlduwanSteve,

1
Non c'è niente di vecchio stile nel nominare un parametro @questo, è sempre stata un'idea orribile :)
Dave Jellison,

2
Preferisco non usare la parola chiave come nomi di variabili a meno che non sia forzata (come @class = "qualcosa" quando lavori con MVC perché hai bisogno di una classe che si riferisce a CSS ma è una parola chiave in C # (ovviamente). Non sono solo io a parlare, è una regola empirica generale. Sebbene sia certamente un codice compilabile e legittimo, pensa anche alla facilità di lettura e digitazione, entrambi i motivi pratici. Sono molto parziale nell'usare i nomi dei parametri per descrivere ciò che sta accadendo prima, quindi ripeti (questo seme di stringa, int count) potrebbe essere un imho più illustrativo
Dave Jellison

1
Interessante, grazie. Penso che in questo caso probabilmente hai ragione che @questo è meno descrittivo di quanto potrebbe essere. Tuttavia, in generale, penso che sia abbastanza comune estendere le classi o le interfacce in cui l'unico nome ragionevole per "questo" è piuttosto generico. Se hai un parametro chiamato 'istanza' o 'it' o 'str' (vedi MSDN ), ciò che probabilmente intendi è 'questo'.
OlduwanSteve

1
Sono sostanzialmente d'accordo con te, ma per amor di discussione ... dato che so di più sulla stringa che sto costruendo, posso fare un lavoro leggermente migliore di StringBuilder. StringBuilder deve fare un'ipotesi quando sta allocando la memoria. Certo, è improbabile che importi in molti scenari, ma qualcuno là fuori potrebbe costruire un numero enorme di stringhe ripetute in parallelo ed essere contento di ottenere alcuni cicli di clock indietro :)
OlduwanSteve

2

Non sono sicuro di come funzionerebbe, ma è un semplice pezzo di codice. (Probabilmente l'ho fatto sembrare più complicato di quello che è.)

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

In alternativa, se conosci il numero massimo di livelli che puoi aspettarti, puoi semplicemente dichiarare un array e indicizzarlo. Probabilmente vorrai rendere questo array statico o costante.

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

2

Non ho abbastanza rappresentante per commentare la risposta di Adam, ma il modo migliore per farlo è così:

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

Devi verificare se numTimes è maggiore di zero, altrimenti otterrai un'eccezione.


2

Non ho visto questa soluzione. Lo trovo più semplice per dove mi trovo attualmente nello sviluppo di software:

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

2

Per uso generale, le soluzioni che coinvolgono la classe StringBuilder sono le migliori per ripetere stringhe multi-carattere. È ottimizzato per gestire la combinazione di un gran numero di stringhe in un modo che la semplice concatenazione non può e che sarebbe difficile o impossibile fare in modo più efficiente a mano. Le soluzioni StringBuilder mostrate qui utilizzano O (N) iterazioni per il completamento, un tasso fisso proporzionale al numero di volte che viene ripetuto.

Tuttavia, per un numero molto elevato di ripetizioni o in cui è necessario ottenere livelli elevati di efficienza, un approccio migliore è fare qualcosa di simile alla funzionalità di base di StringBuilder ma produrre copie aggiuntive dalla destinazione piuttosto che dalla stringa originale, come sotto.

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

Questo raddoppia la lunghezza della stringa di origine / destinazione con ogni iterazione, il che evita il sovraccarico di reimpostare i contatori ogni volta che passa attraverso la stringa originale, invece di leggere e copiare senza problemi la stringa ora molto più lunga, qualcosa che i moderni processori possono fare molto Più efficiente.

Utilizza un logaritmo in base 2 per trovare quante volte è necessario raddoppiare la lunghezza della stringa e quindi procede a farlo più volte. Poiché il resto da copiare è ora inferiore alla lunghezza totale da cui sta copiando, può quindi semplicemente copiare un sottoinsieme di ciò che ha già generato.

Ho usato il metodo Array.Copy () sull'uso di StringBuilder, poiché una copia del contenuto di StringBuilder in se stesso avrebbe il sovraccarico di produrre una nuova stringa con quel contenuto con ogni iterazione. Array.Copy () evita questo, pur continuando a operare con un tasso di efficienza estremamente elevato.

Questa soluzione richiede il completamento delle iterazioni O (1 + log N) , una velocità che aumenta logaritmicamente con il numero di ripetizioni (raddoppiare il numero di ripetizioni equivale a un'iterazione aggiuntiva), un notevole risparmio rispetto agli altri metodi, che aumentano in modo proporzionale.


1

Un altro approccio è considerare stringcome IEnumerable<char>e avere un metodo di estensione generico che moltiplica gli elementi in una raccolta per il fattore specificato.

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

Quindi nel tuo caso:

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

1

Stampa una riga con ripetizione.

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

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


Perché non hai usato Console.WriteLine invece di aggiungere \ n alla fine della stringa?
InxaneNinja,

-1

È possibile creare un ExtensionMethod per farlo!

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

O usando la soluzione @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))
  }
}

3
Vedi il mio commento alla risposta di Kyle
Thomas Levesque,
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.