Чи є простий спосіб повернути рядок, повторений 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: -----

7
Дякуємо, що нагадали мені про цю приємну маленьку особливість. Я думаю, що це загубилося в куточку моєї думки.
Chris Shouts

3
Оскільки метахарактер вкладки '\ t' є символом, це працює і для відступу.
cori

91
Це дуже корисна хитрість C #, але назва запитання задається про рядок (не про знаку). Інші відповіді нижче цієї фактично відповідають на питання, але оцінюються набагато нижче. Я не намагаюся зневажати відповідь Ахмада, але думаю, що або назву цього питання слід змінити (якщо питання насправді стосується персонажів), або інші відповіді дійсно повинні бути використані (а не ця).
pghprogrammer4

14
@Ahmad Як я вже говорив, я не подав заявки, тому що я не вважаю, що ви надали гарну інформацію, а тому, що надана вами інформація не має значення для відповіді на запитання, як зазначено в даний час . Уявіть, що ви шукали спосіб повторення рядків (а не символів), і коли ви перейдете до відповідного питання, вам потрібно прокрутити кілька «корисних» порад, щоб дійти до фактичних відповідей на питання. Якщо суботник порушує вас, я можу його усунути. Але ти бачиш, звідки я родом? Я тут абсолютно не базуюсь?
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.Eumerable тощо як вихід, це тому, що ви (добре, це був я) були надто швидкими, щоб скопіювати / вставити та змінити фрагмент. Якщо вам потрібно накреслити інший рядок на висновку «Перелічених». Повторіть, вам потрібно зробити це так: 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цілком і створити знак [] потрібного розміру відразу.
dtb

@dtb: Мені цікаво, чому ти хотів би це зробити. Я не можу уявити, що це було б набагато швидше (якщо є) швидше, ніж використання A StringBuilder, і код був би менш прозорим.
Адам Робінсон

@dtb: юк. Використовуйте StringBuilder; це було покликано зробити це ефективно.
ToolmakerSteve

Адам, я бачу, ви перейшли до проблеми тестування введення на null, коли додали його до підрахунку, але потім дозволили пропустити це, покладаючись на Апенд, щоб нічого не робити з огляду на нуль. IMHO це менш очевидно: якщо 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). Крім простоти, вона має такі особливості, які не всі інші обговорювані методи поділяють:

  • Повторення рядка будь-якої довжини, а не лише символу (на вимогу ОП).
  • Ефективне використання попереднього розміщення StringBuilderчерез зберігання.

Я згоден, це чудово. Я щойно додав це до свого коду з кредитом Бобу! Дякую!
Джон Берлі

22

Використовуйте String.PadLeft , якщо бажаний рядок містить лише одну таблицю.

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

Кредит тут належить


1
KISS. Для чого нам потрібні StringBuilders та розширення? Це дуже проста проблема.
DOK

1
Я не знаю, це, здається, вирішує проблему так, як це висловлено з деякою елегантністю. І btw, кого ти називаєш "S"? :-)
Стів Таунсенд

7
Але не це на самому справі просто менш очевидною версії в stringконструктор , який приймає charіint ?
Дан Дао

Це буде добре виглядати зі String.Empty замість "" ... в іншому випадку, хороше рішення;)
Thomas Levesque

@Steve BTW 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
Якщо я знайду цей фрагмент у своєму проекті, я буду цікавитись, хто порушує KISS і чому ... приємно інакше
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
Немає нічого старої школи в тому, щоб назвати параметр @ це, це завжди була жахлива ідея :)
Дейв Джеллісон

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

1
Цікаво, дякую. Я думаю, що в цьому випадку ви, мабуть, маєте рацію, що @ це менш описово, ніж могло б бути. Однак в цілому, я думаю, що це досить поширене розширення класів або інтерфейсів, де єдине розумне ім'я для 'цього' є досить загальним. Якщо у вас є параметр, який називається "екземпляр" або "він" або "str" ​​(див. MSDN ), то, напевно, ви мали на увазі "це".
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

У мене не вистачає респондентів, щоб коментувати відповідь Адама, але найкращий спосіб зробити це, як це:

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.