Czy istnieje prosty sposób na zwrócenie ciągu powtórzonego X razy?


318

Próbuję wstawić pewną liczbę wcięć przed ciągiem na podstawie głębokości elementów i zastanawiam się, czy istnieje sposób na zwrócenie ciągu powtórzonego X razy. Przykład:

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
Nie jestem pewien, czy ma to zastosowanie, ale możesz również spojrzeć na IndentedTextWriter .
Chris Shouts

Czy chcesz rozważyć zmianę zaakceptowanej odpowiedzi, ponieważ twoje pytanie dotyczy powtarzania ciągów znaków, a nie znaków, które przedstawia zaakceptowana odpowiedź? Od dzisiaj do .Net v4.7, żaden przeciążony konstruktor Stringklasy nie przyjmuje ciągu jako parametru do tworzenia powtórzeń z niego, co mogłoby uczynić zaakceptowaną odpowiedź idealnym dopasowaniem.
RBT

Odpowiedzi:


537

Jeśli zamierzasz powtarzać tylko ten sam znak, możesz użyć konstruktora łańcuchów, który akceptuje znak i liczbę powtórzeń new String(char c, int count).

Na przykład, aby powtórzyć myślnik pięć razy:

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

7
Dzięki za przypomnienie mi o tej miłej, małej funkcji. Myślę, że zagubiłem się w moim umyśle.
Chris Shouts

3
Ponieważ metaznak tabulatora „\ t” jest znakiem, działa to również na wcięcie.
cori

91
Jest to bardzo przydatna sztuczka w języku C #, ale tytuł pytania dotyczy łańcucha (nie znaku). Inne odpowiedzi poniżej tego są w rzeczywistości odpowiedzią na pytanie, ale są oceniane znacznie niżej. Nie staram się lekceważyć odpowiedzi Ahmada, ale myślę, że albo tytuł tego pytania powinien zostać zmieniony (jeśli pytanie dotyczy znaków), albo inne odpowiedzi powinny zostać poddane pozytywnej ocenie (a nie tej).
pghprogrammer4

14
@Ahmad Jak powiedziałem, nie głosowałem za odrzuceniem, ponieważ nie sądzę, że dostarczyłeś dobrych informacji, ale ponieważ podane przez ciebie informacje nie mają związku z odpowiedzią na pytanie, jak podano obecnie . Wyobraź sobie, że szukałeś sposobu na powtarzanie ciągów znaków (a nie znaków), a po przejściu do odpowiedniego pytania musisz przewinąć kilka „przydatnych” wskazówek, aby uzyskać rzeczywiste odpowiedzi na pytanie. Jeśli głosowanie jest dla ciebie obraźliwe, mogę je usunąć. Ale widzisz, skąd pochodzę? Czy jestem tu całkowicie poza bazą?
pghprogrammer4

11
@ pghprogrammer4 IMHO, opinie negatywne mogą być dość zniechęcające i powinny być stosowane tylko w przypadku szkodliwych / wprowadzających w błąd / naprawdę złych odpowiedzi, które wymagają poważnej zmiany lub powinny zostać usunięte przez ich autora. Odpowiedzi istnieją, ponieważ ludzie są hojni i chcą podzielić się tym, co wiedzą. Ogólnie rzecz biorąc, te witryny naprawdę zachęcają do wyrażania opinii, aby przynieść użyteczne odpowiedzi na górę.
ToolmakerSteve

271

Jeśli korzystasz z .NET 4.0, możesz używać string.Concatrazem z Enumerable.Repeat.

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

W przeciwnym razie wybrałbym coś w rodzaju odpowiedzi Adama .

Powodem, dla którego generalnie nie radziłbym używać odpowiedzi Andreya, jest po prostu to, że ToArray()połączenie wprowadza zbędny narzut, którego unika się w StringBuilderpodejściu zaproponowanym przez Adama. To powiedziawszy, przynajmniej działa bez wymagania .NET 4.0; i to jest szybkie i łatwe (i nie zabije cię, jeśli wydajność nie stanowi większego problemu).


3
Działa to dobrze z łańcuchami - na przykład, jeśli trzeba połączyć spacje niełamliwe (& nbsp;). Ale zakładam, że konstruktor strun będzie najszybszy, jeśli masz do czynienia z char ...
woodbase

1
Konstruktor łańcucha będzie działał tylko dla jednego znaku. na opcję użycia Concat Repeat będzie działać na ciągi znaków
Eli Dagan

4
Dziękujemy za odpowiedź na pytanie tytułowe!
Mark K Cowan

Podoba mi się ta odpowiedź!
Ji_in_coding

Jeśli otrzymujesz -System.Linq.Enumerable itp. Jako wynik, to dlatego, że (ok, to byłem ja) byłeś zbyt szybki, aby skopiować / wkleić i zmodyfikować fragment kodu. Jeśli chcesz powiązać inny ciąg z danymi wyjściowymi Enumerable. Powtórz, musisz to zrobić w następujący sposób: 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
Równie dobrze może przejść input.Length * countdo StringBuilderkonstruktora, nie sądzisz?
Dan Tao

A jeśli przejdziesz input.Length * countdo StringBuilderkonstruktora, możesz StringBuildercałkowicie pominąć i od razu utworzyć znak char [] o odpowiednim rozmiarze.
dtb

@dtb: Jestem ciekawy, dlaczego chcesz to zrobić. Nie mogę sobie wyobrazić, że byłoby to dużo (jeśli w ogóle) szybsze niż użycie a StringBuilder, a kod byłby mniej przejrzysty.
Adam Robinson

@dtb: fuj. Użyj StringBuilder; został zaprojektowany, aby to zrobić skutecznie.
ToolmakerSteve

Adam, widzę, że zadałeś sobie trud testowania danych wejściowych pod kątem wartości zerowej podczas dodawania ich do liczenia, ale potem pozwoliłeś, by to się nie powiedzieło, polegając na Append, aby nic nie robiło, biorąc pod uwagę wartość zerową. IMHO jest to mniej niż oczywiste: jeśli możliwe jest wprowadzenie wartości null, dlaczego nie przetestować tego z góry i zwrócić pusty ciąg?
ToolmakerSteve

46

najbardziej wydajne rozwiązanie dla łańcucha

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

1
Na pewno jest ładna, jednoliniowa. Na pewno nie najbardziej wydajny. StringBuilder ma trochę narzutu na bardzo małą liczbę konkatenacji.
Teejay

25

W wielu scenariuszach jest to prawdopodobnie najładniejsze rozwiązanie:

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

Użycie jest wtedy:

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

Opiera się to na innych odpowiedziach (szczególnie @ c0rd's). Oprócz prostoty ma następujące funkcje, które nie są wspólne dla wszystkich innych omawianych technik:

  • Powtórzenie ciągu dowolnej długości, a nie tylko znaku (zgodnie z żądaniem PO).
  • Efektywne wykorzystanie StringBuilderwstępnej alokacji pamięci.

Zgadzam się, to jest cudowne. Właśnie dodałem to do mojego kodu dzięki podziękowaniu dla Boba! Dzięki!
John Burley,

22

Użyj String.PadLeft , jeśli żądany ciąg zawiera tylko jeden znak.

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

Kredyt należny tutaj


1
POCAŁUNEK. Do czego w każdym razie potrzebujemy StringBuilders i rozszerzenia? To bardzo prosty problem.
DOK

1
Nie wiem, wydaje się, że to rozwiązuje problem z pewną elegancją. A przy okazji, kogo nazywacie „S”? :-)
Steve Townsend

7
Ale nie jest to tak naprawdę mniej oczywiste wersja do stringkonstruktora, który trwa chariint ?
Dan Tao

Ładnie wyglądałby z String.Empty zamiast „” ... inaczej dobre rozwiązanie;)
Thomas Levesque,

@ Steve BTW String.PadLeftargumenty są odwrócone. Występuje countprzed dopełnieniem postaci.
Ahmad Mageed,

17

Możesz powtórzyć swój ciąg (w przypadku, gdy nie jest to pojedynczy znak) i zsumować wynik w następujący sposób:

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

12

Chciałbym uzyskać odpowiedź Dana Tao, ale jeśli nie używasz .NET 4.0, możesz zrobić coś takiego:

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

3
Jeśli znajdę ten fragment w moim projekcie, zastanawiam się, kto narusza KISS i dlaczego ... miło inaczej
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: Myślę, że musisz to zrobić w wersji wcześniejszej niż NET 4.0.
Dan Tao

3

Podoba mi się udzielona odpowiedź. Jednak wzdłuż linii sames używam tego, co kiedyś:

„” .PadLeft (3 * wcięcie, „-”)

Spowoduje to utworzenie wcięcia, ale technicznie chodziło o powtórzenie łańcucha. Jeśli wcięcie ciągu jest czymś w rodzaju> - <, to i zaakceptowana odpowiedź nie zadziałałoby. W tym przypadku rozwiązanie c0rd korzystające z programu budującego łańcuchy wygląda dobrze, chociaż narzut programu budującego łańcuchy może w rzeczywistości nie uczynić go najbardziej wydajnym. Jedną z opcji jest zbudowanie tablicy ciągów, wypełnienie jej ciągami wcięć, a następnie połączenie. Do odrobiny:

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

Wszyscy kochamy sprytne rozwiązanie, ale czasem proste jest najlepsze.


3

Dodanie metody rozszerzenia Korzystam z wszystkich moich projektów:

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

Mam nadzieję, że ktoś może z niego skorzystać ...


2

Zaskoczony nikt nie poszedł do starej szkoły. Nie zgłaszam żadnych roszczeń dotyczących tego kodu, ale dla zabawy:

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

Oczywiście zostawiłem to jako test dla bystrego czytelnika :) Bardzo bystry, dzięki.
OlduwanSteve

1
Nie ma nic oldschoolowego w nazywaniu parametru @ to, to zawsze był okropny pomysł :)
Dave Jellison,

2
Wolę nie używać słowa kluczowego jako nazw zmiennych, chyba że jest to wymuszone (np. @Class = "coś" podczas pracy z MVC, ponieważ potrzebujesz klasy odwołującej się do CSS, ale jest to słowo kluczowe w C # (oczywiście). To nie tylko ja mówię, jest to ogólna zasada. Chociaż z pewnością jest to kod kompilowalny i zgodny z prawem, pomyśl także o łatwości czytania i pisania, zarówno z praktycznych powodów. Bardzo nie podoba mi się używanie nazw parametrów do opisywania tego, co dzieje się najpierw, więc Powtórz (ten ciąg znaków, liczba całkowita) może być bardziej obrazowym imho
Dave Jellison

1
Ciekawe dzięki. Myślę, że w tym przypadku prawdopodobnie masz rację, że @to jest mniej opisowe niż mogłoby być. Ogólnie jednak myślę, że dość powszechne jest rozszerzanie klas lub interfejsów, w których jedyna rozsądna nazwa „to” jest raczej ogólna. Jeśli masz parametr o nazwie „instancja” lub „it” lub „str” (zobacz MSDN ), prawdopodobnie chodziło o „to”.
OlduwanSteve,

1
Zasadniczo się z tobą zgadzam, ale ze względu na argument ... skoro wiem więcej o budowanym przeze mnie łańcuchu znaków, mogę wykonać znacznie lepszą pracę niż StringBuilder. StringBuilder musi zgadywać, kiedy przydziela pamięć. To prawda, że ​​w wielu scenariuszach nie ma to większego znaczenia, ale ktoś może budować ogromną liczbę powtarzających się ciągów równolegle i chętnie odzyska kilka cykli zegara z powrotem :)
OlduwanSteve

2

Nie jestem pewien, jak by to działało, ale jest to łatwy kawałek kodu. (Prawdopodobnie sprawiłem, że wydaje się to bardziej skomplikowane niż jest.)

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

Alternatywnie, jeśli znasz maksymalną liczbę poziomów, której możesz oczekiwać, możesz po prostu zadeklarować tablicę i indeksować do niej. Prawdopodobnie chciałbyś, aby ta tablica była statyczna lub stała.

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

2

Nie mam wystarczającej liczby przedstawicieli, aby skomentować odpowiedź Adama, ale najlepszym sposobem na zrobienie tego imo jest:

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

Musisz sprawdzić, czy numTimes jest większy od zera, w przeciwnym razie otrzymasz wyjątek.


2

Nie widziałem tego rozwiązania. Uważam, że jest to prostsze w przypadku, gdy obecnie zajmuję się tworzeniem oprogramowania:

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

2

Do ogólnego użytku rozwiązania obejmujące klasę StringBuilder są najlepsze do powtarzania ciągów wieloznakowych. Jest zoptymalizowany do obsługi kombinacji dużej liczby ciągów w sposób, którego nie może wykonać proste łączenie, a ręczne lub bardziej wydajne byłoby trudne lub niemożliwe. Pokazane tutaj rozwiązania StringBuilder wykorzystują iteracje O (N) do zrobienia, ryczałt proporcjonalny do liczby powtórzeń.

Jednak w przypadku bardzo dużej liczby powtórzeń lub gdy trzeba z niego wycisnąć wysoki poziom wydajności, lepszym podejściem jest zrobienie czegoś podobnego do podstawowej funkcjonalności StringBuilder, ale tworzenie dodatkowych kopii z miejsca docelowego, a nie z oryginalnego ciągu, jak poniżej.

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

Podwaja to długość ciągu źródłowego / docelowego przy każdej iteracji, co oszczędza narzutów związanych z resetowaniem liczników za każdym razem, gdy przejdzie on przez oryginalny ciąg, zamiast płynnie czytając i kopiując teraz znacznie dłuższy ciąg, co współczesne procesory mogą wiele zrobić wydajniej.

Używa logarytmu base-2, aby dowiedzieć się, ile razy musi podwoić długość ciągu, a następnie tyle razy. Ponieważ pozostała część do skopiowania jest teraz mniejsza niż całkowita długość, z której kopiuje, może po prostu skopiować podzbiór tego, co już wygenerował.

Użyłem metody Array.Copy () nad użyciem StringBuilder, ponieważ kopiowanie zawartości StringBuilder do samego siebie wiązałoby się z koniecznością tworzenia nowego ciągu z tą zawartością przy każdej iteracji. Array.Copy () unika tego, a jednocześnie działa z bardzo wysoką wydajnością.

To rozwiązanie wymaga wykonania iteracji O (1 + log N) , której szybkość wzrasta logarytmicznie wraz z liczbą powtórzeń (podwojenie liczby powtórzeń równa się jednej dodatkowej iteracji), co oznacza znaczne oszczędności w porównaniu z innymi metodami, które zwiększają się proporcjonalnie.


1

Innym podejściem jest rozważenie, stringjak IEnumerable<char>i posiada ogólną metodę rozszerzenia, które będzie pomnożyć elementy w kolekcji przez określony współczynnik.

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

Więc w twoim przypadku:

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

1

Wydrukuj linię z powtórzeniami.

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

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


Dlaczego nie użyłeś Console.WriteLine zamiast dołączać \ n na końcu ciągu?
InxaneNinja

-1

Aby to zrobić, możesz utworzyć ExtensionMethod!

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

Lub używając rozwiązania @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
Zobacz mój komentarz do odpowiedzi Kyle'a
Thomas Levesque,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.