Czy mogę „pomnożyć” ciąg (w C #)?

136

Załóżmy, że mam na przykład ciąg

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

Zasadniczo chcę napisać to wiele razy, w zależności od jakiejś wartości całkowitej.

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

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

EDYCJA: Wiem, że mogę łatwo napisać własną funkcję, aby to zaimplementować, po prostu zastanawiałem się, czy jest jakiś dziwny operator ciągu, o którym nie wiedziałem

pomimo
źródło

Odpowiedzi:

222

W .NET 4 możesz to zrobić:

String.Concat(Enumerable.Repeat("Hello", 4))
Will Dean
źródło
9
I to niewiele więcej w .NET 3.5: String.Concat (Enumerable.Repeat ("Hello", 4) .ToArray ())
Mark Foreman
4
Myślę, że wcale nie jest elegancki. W Pythonie kod, który to robi to: snip * multiplier (to nie jest straszne ... ale też nie jest piękne).
obłąkany jeż
7
@dementedhedgehog Wiem, że zdajesz sobie sprawę z własnej demencji, ale nawet cierpiąc podczas tego cierpienia, możesz zauważyć, że przykład w Pythonie nie byłby dobrą odpowiedzią na pytanie w C # ... Myślę, że większość z nas to widzi że wybór języka jest zawsze kompromisem i prawie każda odpowiedź na jakiekolwiek pytanie może być opatrzona przypisami, zarówno za, jak i przeciw.
Will Dean
1
@will Dean: Właśnie nawiązałem do wypowiedzi Matthew Nicholsa na temat elegancji kodu, który tam masz. O ile wiem, twój kod jest elegancki dla C #, tak jak obecnie, dla tego problemu. Chodzi mi o to, że: (wierzę) byłoby bardzo łatwe (dla firmy Microsoft, ponieważ ciągi są zapieczętowane), aby rozszerzyć C # w celu przeciążenia operatora *, aby umożliwić mnożenie ciągów int *. Co byłoby wtedy rzeczywiście eleganckie w jakimś uniwersalnym sensie, imho, a nie tylko eleganckie w kontekście ograniczeń narzuconych przez C #, jaki istnieje w tej chwili.
obłąkany jeż
@dementedhedgehog Dla mnie jest to sprzeczne z tym, co operator*reprezentuje plik binarny . Chyba każdy z nich.
TEK
99

Zwróć uwagę, że jeśli Twój „ciąg” jest tylko pojedynczym znakiem, konstruktor stringów jest przeciążony, aby go obsłużyć:

int multipler = 10;
string TenAs = new string ('A', multipler);
James Curran
źródło
To jest naprawdę pro.
Zaven Zareyan
61

Niestety / na szczęście klasa string jest zapieczętowana, więc nie można po niej dziedziczyć i przeciążać operatora *. Możesz jednak utworzyć metodę rozszerzenia:

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
źródło
5
Właśnie tam, gdzie szedłem! Prawdopodobnie można by zoptymalizować za pomocą mnożnika source.Length * w module StringBuilder
Marc Gravell
2
Komentarz Marca właśnie gdzie ja jechałem :)
Jon Skeet
1
Potrzebujesz (mnożnik source.Length *), a nie tylko (mnożnik)
Marc Gravell
1
Bardzo pewny. Przydziela ciąg (o tej długości) za kulisami, a następnie mutuje go. To nie działa jak zwykły List <T> itp.
Marc Gravell
1
Metoda przedłużania jest tutaj idealna.
Chris Ballance,
12

Jestem z DrJokepu w tej sprawie , ale jeśli z jakiegoś powodu chciałeś oszukiwać za pomocą wbudowanej funkcjonalności, możesz zrobić coś takiego:

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

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

Lub, jeśli używasz .NET 4:

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

Osobiście nie zawracałbym sobie głowy - niestandardowa metoda rozszerzenia jest znacznie przyjemniejsza.

LukeH
źródło
1
Nigdy nie wiedziałem, że istnieje taki oszustwo. =)
Jronny
10

Gwoli ścisłości - oto inny sposób na zrobienie tego:

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

Myślę, że jakiś czas temu wyciągnąłem to z Stack Overflow, więc to nie jest mój pomysł.

user51710
źródło
9

Musiałbyś napisać metodę - oczywiście w C # 3.0 może to być metoda rozszerzająca:

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

następnie:

string bar = "abc";
string foo = bar.Repeat(2);
Marc Gravell
źródło
Czy nawet .NET3 nie miał Enumerable.Repeat?
Will Dean,
@Will - .NET 3 był WCF / WPF itp., Więc nie; istnieje w .NET 3.5, ale wtedy też byś go potrzebował string.Join- dlaczego nie po prostu zapętlić n razy? O wiele bardziej bezpośredni.
Marc Gravell
Dzięki - nie myślałem właściwie o 3.0 vs 3.5. A jeśli chodzi o to, dlaczego po prostu nie używać pętli, to z pewnością jest to cała istota debaty funkcjonalnej i imperatywnej? Opublikowałem odpowiedź .net 4 niżej, która, jak sądzę, nie jest taka zła w debacie „funkcjonalny spryt” i „zapętlona oczywistość”.
Will Dean
8

Trochę późno (i dla zabawy), jeśli naprawdę chcesz użyć *operatora do tej pracy, możesz zrobić to:

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

A więc:

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

Należy pamiętać, że tak długo, jak jesteś w stanie znaleźć rozsądne zachowanie dla nich, można również obsługiwać inne podmioty za pośrednictwem StringWrapklasy, jak \, ^, %itp ...

PS:

Multiply()rozszerzenie kredytów do @DrJokepu wszelkie prawa zastrzeżone ;-)

digEmAll
źródło
7

To jest o wiele bardziej zwięzłe:

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

W using System.Text;takim przypadku należy zaimportować przestrzeń nazw .

user734119
źródło
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
źródło
2

Jeśli masz .Net 3.5, ale nie 4.0, możesz użyć System.Linq

String.Concat(Enumerable.Range(0, 4).Select(_ => "Hello").ToArray())
Frank Schwieterman
źródło
Aby uzyskać pojedynczy ciąg, którego nadal potrzebujesz String.Concat, a na 3.5 również potrzebujesz .ToArray(). Obawiam się, że nie jest to najbardziej eleganckie rozwiązanie.
Kobi,
2

Ponieważ każdy dodaje własne przykłady .NET4 / Linq, równie dobrze mogę dodać własne. (Zasadniczo to DrJokepu's, zredukowana do jednej linijki)

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
źródło
0

OK, oto moje podejście do sprawy:

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

Jestem oczywiście trochę głupi, ale kiedy potrzebuję tabulacji w klasach generujących kod, Enumerable.Repeat robi to za mnie. I tak, wersja StringBuilder też jest w porządku.

Dmitri Nesteruk
źródło
0

Oto moje podejście do tego w przyszłości:

    /// <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
źródło
Mam nadzieję, że tak naprawdę nie używasz ParallelEnumerablew takich sytuacjach. string.Joinmusi używać elementów w kolejności, więc równoległe ich generowanie nie jest potrzebne.
Gabe
@ Gabe: Ponieważ elementy są takie same i są po prostu kopiami thisString, nie ma potrzeby martwić się o zamówienie tutaj, jak sądzę.
Jronny
Zgadzam się z wieloma nauczycielami w tej dziedzinie, że zazwyczaj dobrze jest kodować, aby powiedzieć, co masz na myśli. Nie ma korzyść, że chcesz to równolegle i tylko potajemnie myśleć „Dobrze wiem, że nie będzie równolegle w tym konkretnym przypadku i tak”
sehe
Myślę, że zrobienie tego równolegle przyspieszy to, albo proszę, oświeć mnie. Dlatego konwertuję to na ParallelEnumerable, więc myślę, że koduję, aby powiedzieć, co mam na myśli ... Dzięki.
Jronny