Gibt es eine einfache Möglichkeit, eine Zeichenfolge zurückzugeben, die X-mal wiederholt wurde?

318

Ich versuche, eine bestimmte Anzahl von Einrückungen vor einer Zeichenfolge basierend auf der Tiefe eines Elements einzufügen, und frage mich, ob es eine Möglichkeit gibt, eine Zeichenfolge zurückzugeben, die X-mal wiederholt wird. Beispiel:

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
quelle
9
Sie sind sich nicht sicher, ob dies zutrifft, aber Sie können sich auch den IndentedTextWriter ansehen .
Chris Shouts
Möchten Sie die akzeptierte Antwort ändern, da es bei Ihrer Frage um die Wiederholung von Zeichenfolgen und nicht um Zeichen geht, die in der akzeptierten Antwort dargestellt sind? Bis heute .Net v4.7 verwendet kein überladener Konstruktor einer StringKlasse eine Zeichenfolge als Parameter, um daraus Wiederholungen zu erstellen, die die akzeptierte Antwort zu einer perfekten Anpassung hätten machen können.
RBT

Antworten:

536

Wenn Sie nur dasselbe Zeichen wiederholen möchten, können Sie den Zeichenfolgenkonstruktor verwenden , der ein Zeichen akzeptiert, und wie oft es wiederholt werden soll new String(char c, int count).

Um beispielsweise einen Bindestrich fünfmal zu wiederholen:

string result = new String('-', 5);
Output: -----
Ahmad Mageed
quelle
7
Vielen Dank, dass Sie mich an dieses nette kleine Feature erinnert haben. Ich denke, es war in meinem Kopf verloren.
Chris Shouts
3
Da das Tabulator-Metazeichen '\ t' ein Zeichen ist, funktioniert dies auch für den Einzug.
Cori
91
Dies ist ein sehr nützlicher Trick von C #, aber der Titel der Frage bezieht sich auf eine Zeichenfolge (kein Zeichen). Die anderen Antworten unter dieser Frage beantworten die Frage tatsächlich, werden jedoch viel niedriger bewertet. Ich versuche nicht, Ahmads Antwort zu missachten, aber ich denke, entweder sollte der Titel dieser Frage geändert werden (wenn sich die Frage tatsächlich auf Charaktere bezieht) oder die anderen Antworten sollten wirklich positiv bewertet werden (und nicht diese).
pghprogrammer4
14
@Ahmad Wie gesagt, ich habe nicht abgelehnt, weil ich nicht glaube, dass Sie gute Informationen geliefert haben, sondern weil die von Ihnen bereitgestellten Informationen für die Beantwortung der Frage wie derzeit angegeben nicht relevant sind . Stellen Sie sich vor, Sie suchen nach einer Möglichkeit, Zeichenfolgen (keine Zeichen) zu wiederholen, und wenn Sie zur entsprechenden Frage gelangen, müssen Sie durch mehrere nützliche Tipps scrollen, um zu den tatsächlichen Antworten auf die Frage zu gelangen. Wenn die Ablehnung für Sie strafbar ist, kann ich sie entfernen. Aber siehst du, woher ich komme? Bin ich hier total off-base?
pghprogrammer4
11
@ pghprogrammer4 IMHO können Downvotes ziemlich entmutigend sein und sollten nur für schädliche / irreführende / wirklich schlechte Antworten verwendet werden, die eine größere Änderung erfordern oder von ihrem Autor gelöscht werden sollten. Antworten existieren, weil die Leute großzügig sind und teilen möchten, was sie wissen. Im Allgemeinen sind diese Websites wirklich auf die Ermutigung von Upvotes ausgerichtet, nützliche Antworten nach oben zu bringen.
ToolmakerSteve
271

Wenn Sie .NET 4.0 verwenden, können Sie es string.Concatzusammen mit verwenden Enumerable.Repeat.

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

Sonst würde ich mit so etwas wie Adams Antwort gehen .

Der Grund, warum ich Andreys Antwort im Allgemeinen nicht empfehlen würde , ist einfach, dass der ToArray()Anruf überflüssigen Overhead einführt, der mit dem StringBuildervon Adam vorgeschlagenen Ansatz vermieden wird. Das heißt, zumindest funktioniert es ohne .NET 4.0; und es ist schnell und einfach (und wird Sie nicht töten, wenn Effizienz kein allzu großes Problem darstellt).

Dan Tao
quelle
3
Dies funktioniert gut mit Zeichenfolgen - beispielsweise, wenn Sie nicht unterbrechende Leerzeichen (& nbsp;) zusammenfassen müssen. Aber ich
gehe
1
Der String-Konstruktor funktioniert nur für ein Zeichen. Die Option zur Verwendung von Concat Repeat funktioniert für Strings
Eli Dagan
4
Vielen Dank für die Beantwortung der eigentlichen Titelfrage!
Mark K Cowan
Ich mag diese Antwort!
Ji_in_coding
Wenn Sie -System.Linq.Enumerable usw. als Ausgabe erhalten, liegt dies daran, dass Sie (ok, das war ich) zu schnell waren, um das Snippet zu kopieren / einzufügen und zu ändern. Wenn Sie eine andere Zeichenfolge auf die Ausgabe von Enumerable.Repeat konzentrieren müssen, müssen Sie dies folgendermaßen tun: 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
quelle
6
Könnte auch input.Length * countan den StringBuilderKonstrukteur übergeben werden, meinst du nicht auch?
Dan Tao
Und wenn Sie input.Length * countan den StringBuilderKonstruktor übergeben, können Sie das Ganze überspringen StringBuilderund sofort ein Zeichen [] mit der richtigen Größe erstellen.
dtb
@dtb: Ich bin gespannt, warum du das machen willst. Ich kann mir nicht vorstellen, dass es viel (wenn überhaupt) schneller wäre als die Verwendung von a StringBuilder, und der Code wäre weniger transparent.
Adam Robinson
@dtb: igitt. Verwenden Sie StringBuilder. Es wurde entwickelt, um dies effizient zu tun.
ToolmakerSteve
Adam, ich sehe, Sie haben sich die Mühe gemacht, die Eingabe auf Null zu testen, als Sie sie zur Zählung hinzugefügt haben, aber dann zugelassen, dass diese durchfällt, und sich darauf verlassen, dass Append angesichts der Null nichts unternimmt. IMHO ist dies weniger als offensichtlich: Wenn null eine mögliche Eingabe ist, warum nicht vorher darauf testen und eine leere Zeichenfolge zurückgeben?
ToolmakerSteve
46

leistungsstärkste Lösung für String

string result = new StringBuilder().Insert(0, "---", 5).ToString();
c0rd
quelle
1
Sicher ist ein hübscher Einzeiler. Sicher nicht der performanteste. StringBuilder hat einen geringen Overhead bei sehr wenigen Verkettungen.
Teejay
25

Für viele Szenarien ist dies wahrscheinlich die sauberste Lösung:

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

Verwendung ist dann:

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

Dies baut auf anderen Antworten auf (insbesondere @ c0rds). Neben der Einfachheit weist es die folgenden Merkmale auf, die nicht alle anderen besprochenen Techniken gemeinsam haben:

  • Wiederholung einer Zeichenfolge beliebiger Länge, nicht nur eines Zeichens (wie vom OP angefordert).
  • Effiziente Nutzung der StringBuilderVorbelegung durch Speicher.
Bob Sammers
quelle
Ich stimme zu, das ist wunderbar. Ich habe dies gerade meinem Code hinzugefügt, mit Gutschrift an Bob! Vielen Dank!
John Burley
22

Verwenden Sie String.PadLeft , wenn Ihre gewünschte Zeichenfolge nur ein einziges Zeichen enthält.

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

Gutschrift hier fällig

Steve Townsend
quelle
1
KUSS. Wofür brauchen wir überhaupt StringBuilder und Erweiterungen? Dies ist ein sehr einfaches Problem.
DOK
1
Ich weiß nicht, dies scheint das Problem zu lösen, wie es mit etwas Eleganz formuliert ist. Und übrigens, wen nennst du 'S'? :-)
Steve Townsend
7
Aber ist dies nicht wirklich nur eine weniger offensichtliche Version des stringKonstruktor, der eine nimmt charund einint ?
Dan Tao
Mit String.Empty würde es besser aussehen als "" ... sonst gute Lösung;)
Thomas Levesque
@Steve Übrigens sind die String.PadLeftArgumente umgekehrt. Das countkommt vor dem Polstercharakter.
Ahmad Mageed
17

Sie können Ihre Zeichenfolge wiederholen (falls es sich nicht um ein einzelnes Zeichen handelt) und das Ergebnis wie folgt zusammenfassen:

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

Ich würde mich für die Antwort von Dan Tao entscheiden, aber wenn Sie .NET 4.0 nicht verwenden, können Sie so etwas tun:

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
quelle
3
Wenn ich diesen Ausschnitt in meinem Projekt finde, werde ich mich fragen, wer gegen den KISS verstoßen hat und warum ... sonst schön
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
quelle
1
@Adam: Ich denke, Sie müssten das vor.NET 4.0 tun.
Dan Tao
3

Ich mag die Antwort. In diesem Sinne habe ich in der Vergangenheit Folgendes verwendet:

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

Dies wird das Erstellen eines Einzugs erfüllen, aber technisch gesehen bestand die Frage darin, eine Zeichenfolge zu wiederholen. Wenn der String-Einzug so etwas wie> - <ist, würde dies sowie die akzeptierte Antwort nicht funktionieren. In diesem Fall sieht die Lösung von c0rd mit Stringbuilder gut aus, obwohl der Overhead von Stringbuilder sie möglicherweise nicht zur leistungsstärksten Lösung macht. Eine Möglichkeit besteht darin, ein Array von Zeichenfolgen zu erstellen, es mit Einrückungszeichenfolgen zu füllen und diese dann zu verknüpfen. Zu wissen:

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

Wir alle lieben eine clevere Lösung, aber manchmal ist einfach am besten.

Londo Mollari
quelle
3

Hinzufügen der Erweiterungsmethode, die ich in allen meinen Projekten verwende:

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

Hoffe, jemand kann davon Gebrauch machen ...

Mayer Spitzer
quelle
2

Überrascht ging niemand auf die alte Schule. Ich mache keine Ansprüche bezüglich dieses Codes, sondern nur zum Spaß:

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
quelle
Offensichtlich habe ich das als Test für den klugen Leser hinterlassen :) Sehr klug, danke.
OlduwanSteve
1
Es ist nichts Altes, einen Parameter @this zu benennen, das war schon immer eine schreckliche Idee :)
Dave Jellison
2
Ich bevorzuge es, Schlüsselwörter nicht als Variablennamen zu verwenden, es sei denn, sie werden erzwungen (z. B. @class = "etwas", wenn Sie mit MVC arbeiten, da Sie eine Klasse benötigen, die sich auf CSS bezieht, aber es ist ein Schlüsselwort in C # (natürlich). Es ist eine allgemeine Faustregel. Obwohl es sicherlich kompilierbarer und legitimer Code ist, denken Sie auch an die Leichtigkeit des Lesens und Tippens, beides praktische Gründe. Ich bin sehr parteiisch darin, Parameternamen zu verwenden, um zu beschreiben, was zuerst passiert, also wiederholen Sie (dieser String-Startwert, int count) könnte imho illustrativer sein.
Dave Jellison
1
Interessant, danke. Ich denke, in diesem Fall haben Sie wahrscheinlich Recht, dass @this weniger beschreibend ist, als es sein könnte. Im Allgemeinen denke ich jedoch, dass es durchaus üblich ist, Klassen oder Schnittstellen zu erweitern, bei denen der einzig vernünftige Name für "dies" eher allgemein ist. Wenn Sie einen Parameter namens 'instance' oder 'it' oder 'str' haben (siehe MSDN ), haben Sie wahrscheinlich 'this' gemeint.
OlduwanSteve
1
Grundsätzlich stimme ich Ihnen zu, aber aus Gründen der Argumentation ... da ich mehr über den String weiß, den ich baue, kann ich einen geringfügig besseren Job machen als StringBuilder. StringBuilder muss raten, wann Speicher zugewiesen wird. Zugegeben, es ist unwahrscheinlich, dass es in vielen Szenarien eine Rolle spielt, aber jemand da draußen könnte eine enorme Anzahl wiederholter Zeichenfolgen parallel aufbauen und froh sein, ein paar Taktzyklen zurück zu gewinnen :)
OlduwanSteve
2

Ich bin mir nicht sicher, wie dies funktionieren würde, aber es ist ein einfacher Code. (Ich habe es wahrscheinlich komplizierter erscheinen lassen als es ist.)

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

Wenn Sie die maximale Anzahl von Ebenen kennen, die Sie erwarten können, können Sie alternativ einfach ein Array deklarieren und darin indizieren. Sie möchten dieses Array wahrscheinlich statisch oder konstant machen.

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

Ich habe nicht genug Repräsentanten, um Adams Antwort zu kommentieren, aber der beste Weg, dies imo zu tun, ist folgender:

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

Sie müssen überprüfen, ob numTimes größer als Null ist. Andernfalls wird eine Ausnahme angezeigt.

Gru
quelle
2

Ich habe diese Lösung nicht gesehen. Ich finde es einfacher, wo ich mich gerade in der Softwareentwicklung befinde:

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

Für den allgemeinen Gebrauch eignen sich Lösungen mit der StringBuilder-Klasse am besten zum Wiederholen von Zeichenfolgen mit mehreren Zeichen. Es ist optimiert, um die Kombination einer großen Anzahl von Zeichenfolgen auf eine Weise zu handhaben, die eine einfache Verkettung nicht kann und die von Hand nur schwer oder gar nicht effizienter durchgeführt werden kann. Die hier gezeigten StringBuilder-Lösungen verwenden zum Abschluss O (N) -Iterationen, eine Flatrate, die proportional zur Häufigkeit der Wiederholung ist.

Für eine sehr große Anzahl von Wiederholungen oder wenn ein hohes Maß an Effizienz herausgepresst werden muss, ist es jedoch besser, etwas Ähnliches wie die Grundfunktionalität von StringBuilder zu tun, aber zusätzliche Kopien vom Ziel anstatt von der ursprünglichen Zeichenfolge zu erstellen. wie nachstehend.

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

Dies verdoppelt die Länge der Quell- / Zielzeichenfolge bei jeder Iteration, wodurch der Aufwand für das Zurücksetzen von Zählern bei jedem Durchlaufen der ursprünglichen Zeichenfolge gespart wird. Stattdessen kann die jetzt viel längere Zeichenfolge problemlos gelesen und kopiert werden, was moderne Prozessoren viel tun können effizienter.

Es verwendet einen Basis-2-Logarithmus, um zu ermitteln, wie oft die Länge der Zeichenfolge verdoppelt werden muss, und fährt dann so oft fort. Da der zu kopierende Rest jetzt kleiner ist als die Gesamtlänge, von der kopiert wird, kann er einfach eine Teilmenge dessen kopieren, was er bereits generiert hat.

Ich habe die Array.Copy () -Methode anstelle der Verwendung von StringBuilder verwendet, da das Kopieren des Inhalts des StringBuilder in sich selbst den Aufwand hätte, bei jeder Iteration einen neuen String mit diesem Inhalt zu erstellen. Array.Copy () vermeidet dies und arbeitet dennoch mit einer extrem hohen Effizienz.

Diese Lösung benötigt O (1 + log N) -Iterationen, eine Rate, die logarithmisch mit der Anzahl der Wiederholungen zunimmt (die Verdoppelung der Anzahl der Wiederholungen entspricht einer zusätzlichen Iteration), was eine erhebliche Einsparung gegenüber den anderen Methoden darstellt, die proportional zunehmen.

buchWyrm
quelle
1

Ein weiterer Ansatz ist zu prüfen , stringwie IEnumerable<char>und eine generische Erweiterungsmethode hat , die die Elemente in einer Auflistung um den angegebenen Faktor multiplizieren.

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

Also in deinem Fall:

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

Drucken Sie eine Zeile mit Wiederholung.

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

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

Kamal
quelle
Warum haben Sie Console.WriteLine nicht verwendet, anstatt \ n am Ende der Zeichenfolge anzuhängen?
InxaneNinja
-1

Sie können dazu eine ExtensionMethod erstellen!

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

Oder mit der @ Dan Tao-Lösung:

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
quelle
3
Siehe meinen Kommentar zu Kyles Antwort
Thomas Levesque