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


Odpowiedzi:


222

W .NET 4 możesz to zrobić:

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

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

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

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.


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


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

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


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 .


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

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

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

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.


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

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