Могу ли я «умножить» строку (в 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>";

РЕДАКТИРОВАТЬ: Я знаю, что могу легко написать свою собственную функцию для реализации этого, мне просто интересно, был ли какой-то странный строковый оператор, о котором я не знал

несмотря
источник

Ответы:

224

В .NET 4 вы можете сделать это:

String.Concat(Enumerable.Repeat("Hello", 4))
Уилл Дин
источник
9
И это не намного больше в .NET 3.5: String.Concat (Enumerable.Repeat ("Hello", 4) .ToArray ())
Марк Форман
4
Я не думаю, что это вообще элегантно. В Python код для этого: snip * multiplier (это не ужасно .. но и не красиво).
сумасшедший ёжик
8
@dementedhedgehog Я знаю, что вы признаете свое собственное слабоумие, но даже если вы страдаете так, как вы это делаете, вы могли бы увидеть, что пример Python не дал бы очень хорошего ответа на вопрос C # ... Я думаю, что большинство из нас это понимают этот выбор языка всегда является компромиссом, и практически любой ответ на любой вопрос может сопровождаться защитой, за или против.
Уилл Дин
1
@will dean: Я как раз имел в виду заявление Мэтью Николса об элегантности вашего кода. Насколько я могу судить, ваш код элегантен для C # в его нынешнем виде для решения этой проблемы. Я пытаюсь сказать следующее: (я считаю) было бы очень легко (для Microsoft, поскольку строки запечатаны) расширить C #, чтобы перегрузить оператор *, чтобы разрешить умножение строк int *. Тогда это было бы элегантно в некотором универсальном смысле, imho, а не просто элегантно в контексте ограничений, налагаемых C # в том виде, в каком он существует в данный момент.
сумасшедший ёжик
1
@dementedhedgehog Для меня это противоречит тому, что operator*представляет собой двоичный файл . Думаю, каждому свое.
TEK
100

Обратите внимание, что если ваша «строка» состоит только из одного символа, существует перегрузка конструктора строки для ее обработки:

int multipler = 10;
string TenAs = new string ('A', multipler);
Джеймс Карран
источник
Это действительно профи.
Завен Зареян
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 * в StringBuilder ctor
Марк
2
Комментарий Марка был именно тем, куда я собирался :)
Джон Скит
1
Вам нужен (источник.Длина * множитель), а не просто (множитель)
Марк Грейвелл
1
Очень уверен. Он выделяет строку (такой длины) за кулисами, а затем изменяет ее. Он не работает как обычный List <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, так что это не моя идея.

user51710
источник
9

Вам нужно будет написать метод - конечно, в C # 3.0 это может быть метод расширения:

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

тогда:

string bar = "abc";
string foo = bar.Repeat(2);
Марк Гравелл
источник
Даже в .NET3 не было Enumerable.Repeat?
Уилл Дин
@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, все права защищены ;-)

digEmAll
источник
8

Это намного короче:

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

В using System.Text;этом случае пространство имен следует импортировать.

user734119
источник
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's

String.Concat(Enumerable.Range(0, 4).Select(_ => "Hello").ToArray())
Франк Швитерман
источник
Чтобы получить одну строку, вам все равно понадобится String.Concat, и на 3.5 вам также понадобится .ToArray(). Боюсь, не самое элегантное решение.
Kobi
2

Поскольку каждый добавляет свои собственные примеры .NET4 / Linq, я мог бы добавить свои собственные. (По сути, это DrJokepu, сокращенный до однострочного)

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

Я, конечно, немного глуп, но когда мне нужно табуляцию в классах, генерирующих код, Enumerable.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));
    }
Jronny
источник
Надеюсь, вы действительно не используете ParallelEnumerableв подобных ситуациях. string.Joinнеобходимо использовать элементы по порядку, поэтому распараллеливание их генерации не требуется.
Гейб
@Gabe: Поскольку элементы одинаковы и на самом деле являются копиями thisString, я думаю, нет необходимости беспокоиться о порядке здесь.
Jronny
Я согласен со многими учителями в этой области в том, что обычно полезно писать код, чтобы говорить то, что вы имеете в виду. Нет смысла говорить, что вы хотите эту параллель, и только втайне думаете: «Ну, я знаю, что в этом конкретном случае параллели все равно не будет»
см.
Я думаю, что параллельное выполнение делает его быстрее, или, пожалуйста, просветите меня. Вот почему я конвертирую это в ParallelEnumerable, поэтому я думаю, что кодирую, чтобы сказать то, что я имею в виду ... Спасибо.
Jronny