Чи можу я "помножити" рядок (у C #)?


136

Припустимо, у мене є рядок, наприклад,

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

Я в основному хочу написати це кілька разів, залежно від якогось цілого значення.

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

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

EDIT: Я знаю, що можу легко написати власну функцію, щоб реалізувати це, мені було просто цікаво, чи є якийсь дивний струнний оператор, про який я не знав


Відповіді:


222

У .NET 4 ви можете зробити це:

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

9
І це не набагато більше в .NET 3.5: String.Concat (Enumerable.Repeat ("Привіт", 4) .ToArray ())
Марк Форман

4
Я взагалі не думаю, що це елегантно. У python код для цього є: snip * multiplier (Це не жахливо .. але і це не красиво).
дементований їжак

7
@dementedhedgehog Я знаю, що ти визнаєш свою власну недоумство, але навіть страждаючи, як і ти, ти можеш побачити, що приклад Python не дав би дуже гарного відповіді на питання C # ... Я думаю, що більшість із нас може бачити що вибір мови - це завжди компроміс, і майже будь-яка відповідь на будь-яке питання може бути підкреслена адвокатською діяльністю, або за, або проти.
Буде Дін

1
@will dean: Я щойно мав на увазі заяву Меттью Ніколса про елегантність коду, який у вас є. Наскільки я можу сказати, ваш код є елегантним для C #, як це зараз є, для цієї проблеми. Я намагаюся зробити таке: (я вважаю) було б дуже легко (для Microsoft, оскільки рядки запечатані) розширити C # для перевантаження оператора *, щоб дозволити множення рядка int *. Що б насправді було елегантним у якомусь універсальному сенсі, імхо, не просто елегантне в контексті обмежень, накладених C #, як це існує на даний момент.
дементований їжак

@dementedhedgehog Для мене це суперечить тому, що operator*представляє собою бінарне . Кожен свій, я думаю.
ТЕК

99

Зауважте, що якщо ваш "рядок" є лише одним символом, то в ньому може бути перевантажено конструктор рядків:

int multipler = 10;
string TenAs = new string ('A', multipler);

Це справді про.
Zaven

61

На жаль / на щастя, клас рядків запечатаний, тому ви не можете успадкувати його і перевантажити оператор *. Однак ви можете створити метод розширення:

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
Тільки куди я їхав! Можливо, ви могли б оптимізувати, використовуючи множник source.Length * у ctor StringBuilder
Marc Gravell

2
Коментар Марка був саме туди, куди я їхав :)
Джон Скіт

1
Вам потрібно (source.Length * множник), а не просто (множник)
Marc Gravell

1
Дуже впевнений. Він виділяє рядок (такої довжини) за лаштунками, а потім мутує його. Це не працює як звичайний Список <T> тощо.
Марк Гравелл

1
Тут ідеально підходить метод розширення.
Кріс Баланс

12

Я з DrJokepu на цьому , але якщо ви чомусь захотіли обманювати, використовуючи вбудований функціонал, то ви можете зробити щось подібне:

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

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

Або якщо ви використовуєте .NET 4:

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

Особисто я б не турбувався, хоча - власний метод розширення набагато приємніший.


1
Я ніколи не знав, що є такий чіт. =)
Jronny

10

Просто задля повноти - ось ще один спосіб зробити це:

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

Я думаю, що я витягнув цей із Stack Overflow деякий час тому, тому це не моя ідея.


9

Вам доведеться написати метод - звичайно, для C # 3.0 це може бути метод розширення:

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

тоді:

string bar = "abc";
string foo = bar.Repeat(2);

Навіть .NET3 не мав безліч. Повторіть?
Буде Дін

@Will - .NET 3 був WCF / WPF тощо, тому ні; він існує в .NET 3.5, але тоді вам знадобиться і string.Joinце - чому б не просто циклічити n разів? Набагато пряміший.
Marc Gravell

Дякую - я не думав належним чином про 3.0 проти 3.5. Щодо того, чому б просто не використовувати цикл, безумовно, у цьому вся суть функціональної проти імперативної дискусії? Я розмістив відповідь .net 4 нижче, що, на мою думку, не надто поганий у дискусії про «функціональну кмітливість» проти «очевидності циклу».
Буде Дін

8

Трохи пізно (і просто заради розваги), якщо ви дійсно хочете використовувати *оператора для цієї роботи, ви можете це зробити:

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

І так:

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

Слід зазначити, що до тих пір , як ви можете знайти розумне поведінку для них, ви можете також звертатися з іншими операторами через StringWrapклас, як \, ^,% і т.д. ...

PS:

Multiply()подовження кредитів на @DrJokepu всі права захищені ;-)


7

Це набагато більш стисло:

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

У using System.Text;цьому випадку простору імен слід імпортувати.


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

Якщо у вас є .Net 3.5, але не 4.0, ви можете використовувати System.Linq

String.Concat(Enumerable.Range(0, 4).Select(_ => "Hello").ToArray())

Щоб отримати одну нитку, вам все одно знадобиться String.Concat, а на 3.5 вам також знадобиться .ToArray(). Боюся, не найелегантніше рішення.
Кобі

2

Оскільки кожен додає свої власні приклади .NET4 / Linq, я також можу додати свої власні. (В основному, це д-р Джокепу, зведений до однолінійного)

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

Гаразд, ось я взявся до цього питання:

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

Я, звичайно, трохи нерозумний, але коли мені потрібно мати таблицю в класах, що генерують код, Inumerable.Repeat робить це для мене. І так, версія StringBuilder теж чудово.


0

Ось мій погляд на це лише для подальшого ознайомлення:

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

Я сподіваюся, що ви насправді не використовуєте ParallelEnumerableв таких ситуаціях. string.Joinпотрібно використовувати елементи для того, щоб паралелізувати їх генерацію не потрібно.
Гейб

@Gabe: Оскільки елементи однакові і насправді є лише копіями цього String, я думаю, не потрібно турбуватися про замовлення тут.
Jronny

Я погоджуюся з багатьма викладачами в цій галузі, що зазвичай добре кодувати, щоб сказати, що ти маєш на увазі. Там немає ніякої користі , щоб сказати , що ви хочете цю паралель і тільки таємно думати «Ну, я знаю , що це НЕ буде паралельно в даному конкретному випадку , так чи інакше»
sehe

Я думаю, що зробити його паралельним - це швидше, або, будь ласка, просвітліть мене. Тому я перетворюю це на ParallelEnumerable, тому я думаю, що кодую, щоб сказати, що я маю на увазі ... Дякую.
Jronny
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.