Есть ли простой способ вернуть строку, повторенную X раз?


318

Я пытаюсь вставить определенное количество отступов перед строкой, основываясь на глубине элементов, и мне интересно, есть ли способ вернуть строку, повторенную X раз. Пример:

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
Не уверен, что это применимо, но вы можете посмотреть на IndentedTextWriter .
Крис Кричит

Хотите ли вы рассмотреть вопрос об изменении принятого ответа, поскольку ваш вопрос касается повторения строк, а не символов, которые отображает принятый ответ? Начиная с сегодняшнего дня и до .Net v4.7, любой перегруженный конструктор Stringкласса не принимает строку в качестве параметра для создания из нее повторений, которые могли бы сделать принятый ответ идеальным соответствием.
RBT

Ответы:


537

Если вы собираетесь повторять только один и тот же символ, вы можете использовать строковый конструктор, который принимает символ и количество раз, чтобы повторить его new String(char c, int count).

Например, чтобы повторить тире пять раз:

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

8
Спасибо, что напомнили мне об этой милой маленькой функции. Я думаю, что это было потеряно в моей голове.
Крис Кричит

3
Поскольку метасимвол табуляции '\ t' является символом, это также работает для отступа.
Кори

91
Это очень полезная уловка C #, но заголовок вопроса спрашивает о строке (не символе). Другие ответы ниже этого на самом деле отвечают на вопрос, но оцениваются гораздо ниже. Я не пытаюсь неуважительно относиться к ответу Ахмада, но я думаю, что либо название этого вопроса должно быть изменено (если вопрос на самом деле касается персонажей), либо действительно должны быть проголосованы другие ответы (а не этот).
pghprogrammer4

15
@ Ахмад Как я уже сказал, я не понизил голос, потому что я не думаю, что вы предоставили хорошую информацию, а потому что информация, которую вы предоставили, не имеет отношения к ответу на вопрос, как указано в настоящее время . Представьте, что вы искали способ повторить строки (не символы), и когда вы переходите к соответствующему вопросу, вам нужно прокрутить несколько «полезных» советов, чтобы получить реальные ответы на вопрос. Если отрицательный ответ оскорбляет вас, я могу его убрать. Но ты видишь, откуда я? Я полностью вне базы?
pghprogrammer4

11
@ pghprogrammer4 ИМХО, отрицательные отзывы могут быть весьма обескураживающими, и их следует использовать только для вредных / вводящих в заблуждение / истинно_бадных ответов, которые требуют серьезных изменений или должны быть удалены их автором. Ответы существуют потому, что люди щедры и хотят поделиться тем, что они знают. Как правило, эти сайты действительно поощряют голоса, чтобы принести полезные ответы на вершину.
ToolmakerSteve

271

Если вы используете .NET 4.0, вы можете использовать string.Concatвместе с Enumerable.Repeat.

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

В противном случае я бы пошел с чем-то вроде ответа Адама .

Причина, по которой я бы вообще не советовал использовать ответ Андрея, заключается в том, что ToArray()вызов вводит лишние накладные расходы, которых избегает StringBuilderподход, предложенный Адамом. Тем не менее, по крайней мере, это работает, не требуя .NET 4.0; и это быстро и легко (и не собирается вас убивать, если эффективность не слишком важна).


3
Это будет хорошо работать со строками - например, если вам нужно объединить неразрывные пробелы (& nbsp;). Но я предполагаю, что строковый конструктор будет самым быстрым, если вы имеете дело с
символом

1
Строковый конструктор будет работать только для одного символа. на вариант использования Concat Repeat будет работать для струн
Эли Даган

4
Спасибо за ответ на актуальный заглавный вопрос!
Марк К Коуэн

Мне нравится этот ответ!
Ji_in_coding

Если вы получаете -System.Linq.Enumerable и т. Д. В качестве вывода, это потому, что вы (хорошо, это был я) слишком быстро скопировали / вставили и изменили фрагмент. Если вам нужно объединить другую строку в вывод Enumerable.Repeat, вам нужно сделать это следующим образом: Console.WriteLine(string.Concat("-", string.Concat(Enumerable.Repeat(indent, N))));
Гейб

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
Не могли бы вы перейти input.Length * countк StringBuilderконструктору?
Дэн Тао

И если вы перейдете input.Length * countк StringBuilderконструктору, вы можете StringBuilderвообще пропустить и создать char [] правильного размера.
DTB

@dtb: Мне любопытно, почему ты хочешь это сделать. Я не могу себе представить, что это будет намного (если таковые имеются) быстрее, чем с использованием StringBuilder, и код будет менее прозрачным.
Адам Робинсон

@dtb: гадость. Использовать StringBuilder; это было разработано, чтобы сделать это эффективно.
ToolmakerSteve

Адам, я вижу, вы столкнулись с проблемой проверки входных данных на ноль при добавлении их для подсчета, но затем позволили этому провалиться, полагая, что «Добавить» ничего не делает при нулевом значении. ИМХО, это менее чем очевидно: если возможен ввод null, почему бы не проверить это заранее и вернуть пустую строку?
ToolmakerSteve

46

наиболее производительное решение для строки

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

1
Конечно, это симпатичная строчка. Конечно, не самый производительный. StringBuilder имеет небольшие накладные расходы из-за очень небольшого числа конкатенаций.
Teejay

25

Для многих сценариев это, вероятно, самое подходящее решение:

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

Использование тогда:

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

Это основано на других ответах (особенно @ c0rd's). Помимо простоты, он имеет следующие особенности, которые разделяют не все другие обсуждаемые методы:

  • Повторение строки любой длины, а не просто символа (по запросу OP).
  • Эффективное использование StringBuilderсквозного предварительного хранения.

Я согласен, это замечательно. Я только что добавил это в свой код с благодарностью Бобу! Спасибо!
Джон Берли

22

Используйте String.PadLeft , если желаемая строка содержит только один символ.

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

Кредит здесь


1
ПОЦЕЛУЙ. Для чего нам нужны StringBuilders и расширения? Это очень простая проблема.
ДОК

1
Я не знаю, кажется, это решает проблему, выраженную с некоторой элегантностью. И, кстати, кого ты называешь "S"? :-)
Стив Таунсенд

7
Но не это на самом деле просто менее очевидной версии в stringконструктор , который принимает charиint ?
Дэн Тао

Было бы лучше выглядеть с String.Empty вместо "" ... в противном случае, хорошее решение;)
Томас Левеск

@ Стив Кстати, String.PadLeftаргументы поменялись местами. Перед countпредшествующим символом
Ахмад Магид

17

Вы можете повторить вашу строку (если это не один символ) и объединить результат, например так:

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

12

Я бы взял ответ Дэна Тао, но если вы не используете .NET 4.0, вы можете сделать что-то вроде этого:

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

3
Если я найду этот фрагмент в своем проекте, я буду удивляться, кто нарушает ПОЦЕЛУЙ и почему ... приятно иначе
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: Я полагаю, вам нужно сделать это до .NET 4.0.
Дэн Тао

3

Мне нравится ответ, данный. В том же духе я использовал то же самое в прошлом:

"" .PadLeft (3 * Отступ, '-')

Это выполнит создание отступа, но технически вопрос заключался в повторении строки. Если отступ строки - что-то вроде> - <, то это, а также принятый ответ не будут работать. В этом случае решение c0rd, использующее stringbuilder, выглядит хорошо, хотя накладные расходы на stringbuilder могут фактически не сделать его наиболее производительным. Один из вариантов - создать массив строк, заполнить его строками с отступами, а затем согласовать его. Для того чтобы:

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

Мы все любим умное решение, но иногда лучше просто.


3

Добавляя метод расширения, я использую все свои проекты:

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

Надеюсь, кто-то может использовать это ...


2

Удивлен, никто не пошел в старую школу. Я не делаю никаких претензий по поводу этого кода, а просто для удовольствия:

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

Очевидно, я оставил это в качестве теста для проницательного читателя :) Очень проницательный, спасибо.
OlduwanSteve

1
Нет ничего старого в названии параметра @this, это всегда было ужасной идеей :)
Дейв Джеллисон,

2
Я предпочитаю не использовать ключевое слово в качестве имен переменных, пока не будет принудительно (например, @class = "что-то" при работе с MVC, потому что вам нужен класс, ссылающийся на CSS, но это ключевое слово в C # (конечно). Это не только я, Это общее практическое правило. Хотя это, безусловно, компилируемый и легитимный код, подумайте также об удобстве чтения и ввода, что является практическими причинами. Я очень неравнодушен к использованию имен параметров, чтобы описать то, что происходит вначале, поэтому повторите (это начальное число int count) может быть более наглядным imho.
Дейв Джеллисон

1
Интересно, спасибо. Я думаю, что в этом случае вы, вероятно, правы, что @this менее наглядно, чем могло бы быть. Однако в целом я думаю, что довольно распространено расширение классов или интерфейсов, где единственное разумное имя для «этого» является довольно общим. Если у вас есть параметр с именем «instance» или «it» или «str» (см. MSDN ), то вы, вероятно, имели в виду «this».
OlduwanSteve

1
Я в основном согласен с вами, но ради аргумента ... так как я знаю больше о строящейся строке, я могу сделать немного лучше, чем StringBuilder. StringBuilder должен угадать, когда он выделяет память. Конечно, это вряд ли имеет значение во многих сценариях, но кто-то может построить огромное количество повторяющихся строк параллельно и будет рад получить несколько тактов назад :)
OlduwanSteve

2

Не уверен, как это будет работать, но это легкий кусок кода. (Я, вероятно, заставил это казаться более сложным, чем это.)

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

В качестве альтернативы, если вы знаете максимальное количество уровней, которое вы можете ожидать, вы можете просто объявить массив и индексировать его. Возможно, вы захотите сделать этот массив статическим или постоянным.

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

2

У меня недостаточно представителя, чтобы прокомментировать ответ Адама, но лучший способ сделать это, imo, такой:

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

Вы должны проверить, больше ли numTimes, чем ноль, иначе вы получите исключение.


2

Я не видел этого решения. Я нахожу это проще для того места, где я сейчас занимаюсь разработкой программного обеспечения:

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

2

Для общего использования решения, включающие класс StringBuilder, лучше всего подходят для повторения многосимвольных строк. Он оптимизирован для обработки комбинации большого количества строк так, как это не может сделать простая конкатенация, и что было бы трудно или невозможно сделать более эффективно вручную. Решения StringBuilder, показанные здесь, используют O (N) итераций для завершения, фиксированная скорость, пропорциональная количеству повторений.

Тем не менее, для очень большого числа повторов, или когда из него нужно выжать высокий уровень эффективности, лучшим подходом будет сделать нечто похожее на базовую функциональность StringBuilder, но производить дополнительные копии из места назначения, а не из исходной строки, как ниже.

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

Это удваивает длину строки источника / назначения с каждой итерацией, что экономит накладные расходы на сброс счетчиков при каждом прохождении исходной строки, вместо этого плавно читая и копируя теперь гораздо более длинную строку, что современные процессоры могут сделать очень много более эффективно.

Он использует логарифм base-2, чтобы найти, сколько раз ему нужно удвоить длину строки, а затем продолжает делать это столько раз. Поскольку остаток, подлежащий копированию, теперь меньше общей длины, с которой он копируется, он может просто скопировать подмножество того, что уже сгенерировано.

Я использовал метод Array.Copy (), а не StringBuilder, так как копирование содержимого StringBuilder в себя потребовало бы создания новой строки с этим содержимым с каждой итерацией. Array.Copy () избегает этого, все еще работая с чрезвычайно высокой эффективностью.

Это решение требует O (1 + log N) итераций для завершения, скорость, которая логарифмически увеличивается с числом повторений (удвоение числа повторов равно одной дополнительной итерации), что существенно экономит по сравнению с другими методами, которые увеличиваются пропорционально.


1

Другой подход заключается в рассмотрении , stringкак IEnumerable<char>и есть общий метод расширения , который будет умножать элементы в коллекции с помощью указанного фактора.

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

Итак, в вашем случае:

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

1

Распечатать строку с повторением.

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

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


Почему вы не использовали Console.WriteLine вместо добавления \ n в конце строки?
InxaneNinja

-1

Вы можете создать 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;
  }
}

Или используя решение @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
Смотрите мой комментарий к ответу Кайла
Томас Левеск
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.