X sayısını defalarca döndürmek için kolay bir yol var mı?


318

Bir öğe derinliği dayalı bir dize önce girintiler belirli bir sayı eklemek çalışıyorum ve X kez tekrarlanan bir dize döndürmek için bir yol olup olmadığını merak ediyorum. Misal:

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
Bunun geçerli olup olmadığından emin değilsiniz, ancak IndentedTextWriter'a da bakmak isteyebilirsiniz .
Chris Shouts

Sorunuz kabul edilen cevabın tasvir ettiği karakterlerle değil, dizelerin tekrarı ile ilgili olduğu için kabul edilen cevabı değiştirmeyi düşünmek ister misiniz? Net v4.7'ye kadar, aşırı yüklenmiş herhangi bir Stringsınıf oluşturucu, kabul edilen yanıtı mükemmel bir uyum haline getirebilecek tekrarlar oluşturmak için bir dize parametre olarak almaz.
RBT

Yanıtlar:


538

Yalnızca aynı karakteri tekrarlamak istiyorsanız, bir karakter ve tekrarlama sayısını kabul eden dize yapıcısını kullanabilirsiniz new String(char c, int count).

Örneğin, tire işaretini beş kez tekrarlamak için:

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

8
Bana bu güzel küçük özelliği hatırlattığın için teşekkürler. Bence aklımın köşesinde kayboldu.
Chris Shouts

3
Sekme metakarakter '\ t' bir karakter olduğu için bu girinti için de geçerlidir.
cori

91
Bu C # çok yararlı bir hile, ama sorunun başlığı bir karakter (bir karakter değil) hakkında soruyor. Bunun altındaki diğer cevaplar aslında soruyu cevaplıyor, ancak çok daha düşük puan alıyorlar. Ahmad'ın cevabını saygısızlık etmeye çalışmıyorum, ama ya bu sorunun başlığı değiştirilmeli (soru aslında karakterlerle ilgili ise) ya da diğer cevapların gerçekten kaldırılması gerektiğini düşünüyorum (bu değil).
pghprogrammer4

14
@Ahmad Dediğim gibi, iyi bilgi verdiğinizi düşünmediğim için aşağı itmedim, ancak verdiğiniz bilgiler şu anda belirtilen soruyu cevaplamakla ilgili olmadığından . Dizeleri tekrarlamanın bir yolunu (karakter değil) aradığınızı ve ilgili soruya geldiğinizde, sorunun gerçek cevaplarına ulaşmak için birkaç 'yararlı' ipucunu kaydırmanız gerektiğini düşünün. Downvote size karşı suç ise, kaldırabilirim. Ama nereden geldiğimi görüyor musun? Burada tamamen üs dışı mıyım?
pghprogrammer4

11
@ pghprogrammer4 IMHO, downvotes oldukça cesaret kırıcı olabilir ve sadece büyük bir değişikliğe ihtiyaç duyan veya yazarları tarafından silinmesi gereken zararlı / yanıltıcı / gerçekten_bad yanıtları için kullanılmalıdır. Cevaplar var çünkü insanlar cömertler ve bildiklerini paylaşmak istiyorlar. Genellikle, bu siteler gerçekten yararlı oylar tepeye getirmek için upvotes teşvik ediyor.
ToolmakerSteve

271

.NET 4.0 kullanıyorsanız, string.Concatile birlikte kullanabilirsiniz Enumerable.Repeat.

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

Yoksa Adem'in cevabı gibi bir şeyle giderdim .

Genellikle sebebi olmaz kullanarak tavsiye Andrey yanıtını basitçe olmasıdır ToArray()ile önlenir çağrı tanıtır gereksiz havai StringBuilderyaklaşımı Adam tarafından önerdi. Bununla birlikte, en azından .NET 4.0 gerektirmeden çalışır; ve hızlı ve kolaydır (ve verimlilik çok fazla endişe duymazsa sizi öldürmeyecektir).


3
Bu, dizelerle güzelce çalışır - kırılmayan boşlukları birleştirmeniz gerekiyorsa (& nbsp;). Ama bir char ile uğraşıyorsanız dize yapıcı en hızlı olacağını varsayalım ...
woodbase

1
Dize yapıcısı yalnızca bir karakter için çalışır. Yineleme kullanma seçeneği dizelerde çalışacak
Eli Dagan

4
Gerçek başlık sorusunu cevapladığınız için teşekkürler!
Mark K Cowan

Bu cevabı beğendim!
Ji_in_coding

Çıktı olarak -System.Linq.Enumerable vb. Alıyorsanız, bunun nedeni (tamam, bu bendim) parçacığı kopyalamak / yapıştırmak ve değiştirmek için çok hızlı olmanızdı. Enumerable.Repeat çıkışına farklı bir dize kapatmanız gerekiyorsa, bunu şu şekilde yapmanız gerekir: Console.WriteLine(string.Concat("-", string.Concat(Enumerable.Repeat(indent, N))));
Gabe

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
Yanı geçebilir input.Length * countiçin StringBuilderyapıcı, değil mi?
Dan Tao

Geçtiğiniz takdirde Ve input.Length * counthiç StringBuilderyapıcı, sen atlamak StringBuildertamamen ve hemen doğru boyutta bir char [] oluşturun.
dtb

@dtb: Bunu neden yapmak istediğini merak ediyorum. Kullanmaktan daha hızlı (varsa) StringBuilderve kodun daha az şeffaf olacağını hayal edemiyorum .
Adam Robinson

@dtb: yuck. StringBuilder kullanın; bunu verimli bir şekilde yapmak için tasarlanmıştır.
ToolmakerSteve

Adam, sayıyı eklerken null için sınama sorununa gittiğinizi görüyorum, ancak null verilen hiçbir şeyi yapmamak için Append'e güvenerek düşmesine izin verdiniz. IMHO bu açıkça görülmüyor: null olası bir girdi ise, neden bunu önceden test edip boş bir dize döndürmüyorsunuz?
ToolmakerSteve

46

string için en performanslı çözüm

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

1
Tabii oldukça güzel bir astar. Tabii ki en yüksek performans değil. StringBuilder, çok az sayıda birleşim üzerinde biraz ek yüke sahiptir.
Teejay

25

Birçok senaryo için, bu muhtemelen en iyi çözümdür:

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

Kullanım o zaman:

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

Bu, diğer cevaplar üzerine kuruludur (özellikle @ c0rd's). Basitliğin yanı sıra, tartışılan diğer tüm tekniklerin paylaşmadığı aşağıdaki özelliklere sahiptir:

  • Sadece bir karakter değil, herhangi bir uzunlukta bir dizenin tekrarı (OP tarafından istendiği gibi).
  • StringBuilderDepolama ön yerleşimi sayesinde verimli kullanım .

Katılıyorum, bu harika. Ben sadece Bob kredi ile koduma ekledi! Teşekkürler!
John Burley

22

İstediğiniz dize yalnızca tek bir karakter içeriyorsa String.PadLeft kullanın .

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

Kredi vadesi burada


1
ÖPÜCÜK. Zaten StringBuilders ve uzantılarına ne ihtiyacımız var? Bu çok basit bir problem.
DOK

1
Bilmiyorum, bu biraz zerafetle ifade edilen sorunu çözüyor gibi görünüyor. Ve btw, kime 'S' diyorsun? :-)
Steve Townsend

7
Ama bu gerçekten sadece az belirgin sürüm değil bir sürer yapıcısı ve bir ? stringcharint
Dan Tao

"" Yerine String.Empty ile daha güzel görünecektir ... aksi halde, iyi bir çözüm;)
Thomas Levesque

@Steve BTW String.PadLeftargümanları tersine çevrilir. countDolgu karakteri önce gelir.
Ahmad Mageed

17

Dizenizi tekrarlayabilir (tek bir karakter olmaması durumunda) ve sonucu şu şekilde birleştirebilirsiniz:

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

12

Dan Tao'nun cevabı için gitmek istiyorum, ama .NET 4.0 kullanmıyorsanız böyle bir şey yapabilirsiniz:

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

3
Eğer bu pasajı projemde bulabilirsem, KISS'i kimin ihlal ettiğini ve neden ... hoş
karşılanmadığını

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: Sanırım bunu .NET 4.0 öncesi yapmalısın.
Dan Tao

3

Verilen cevabı seviyorum. Sames hatları boyunca geçmişte kullandığım olsa da:

"" .PadLeft (3 * Girinti, '-')

Bu bir girinti oluşturmayı gerçekleştirecektir ancak teknik olarak soru bir dizeyi tekrarlamaktı. Dize girintisi> - <gibi bir şeyse, bu yanı sıra kabul edilen cevap işe yaramaz. Bu durumda, stringbuilder'ı kullanan c0rd'ın çözümü iyi görünüyor, ancak stringbuilder'ın yükü aslında en iyi performansı göstermeyebilir. Bir seçenek, bir dizeler dizisi oluşturmak, onu girinti dizeleriyle doldurmak ve sonra bunu tamamlamaktır. Katlamak için:

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

Hepimiz akıllı bir çözümü severiz, ancak bazen basit en iyisidir.


3

Uzantı Yöntemi Ekleme Projelerimin tamamında kullanıyorum:

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

Umarım birisi bundan faydalanabilir ...


2

Kimse eski okula gitmedi şaşırttı. Bu kod hakkında herhangi bir iddiada bulunmuyorum , ama sadece eğlence için:

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

Açıkçası ben onu zeki okuyucu için bir test olarak bıraktım :) Çok zeki, teşekkürler.
OlduwanSteve

1
Eski okulda bu parametreyi adlandırmakla ilgili hiçbir şey yok, bu her zaman korkunç bir fikirdi :)
Dave Jellison

2
Zorunlu olmadıkça anahtar kelimeyi değişken adları olarak kullanmamayı tercih ediyorum (MVC ile çalışırken cc = "bir şey" gibi) çünkü CSS'ye atıfta bulunan sınıfa ihtiyacınız var, ancak C # 'da (elbette) bir anahtar kelime. Bu genel bir kural.Kesinlikle derlenebilir bir kod ve meşru olmasına rağmen, hem pratik nedenleri hem de okuma ve yazma kolaylığını düşünün.Önce neler olduğunu tanımlamak için parametre adlarını kullanmak için çok kısayım. int count) daha açıklayıcı bir
imho

1
İlginç, teşekkürler. Sanırım bu durumda @ bu olabileceğinden daha az açıklayıcı olabilirsiniz. Ancak genel olarak, 'this' için tek makul adın oldukça genel olduğu sınıfları veya arayüzleri genişletmenin oldukça yaygın olduğunu düşünüyorum. Eğer 'örnek' ya da 'bu' ya da 'str' (bkz. MSDN ) adında bir parametreniz varsa, muhtemelen 'bu'dur.
OlduwanSteve

1
Temelde sana katılıyorum, ama tartışma uğruna ... inşa ettiğim dize hakkında daha fazla şey bildiğim için StringBuilder'dan daha iyi bir iş yapabilirim. StringBuilder bellek ayırırken bir tahmin yapmak zorundadır. Birçok senaryoda önemli değildir, ancak birileri paralel olarak çok sayıda tekrarlanan dizeler inşa ediyor olabilir ve birkaç saat geri dönüş
yapmaktan memnuniyet duyabilir

2

Bunun nasıl performans göstereceğinden emin değilim, ancak kolay bir kod parçası. (Muhtemelen olduğundan daha karmaşık görünmesini sağladım.)

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

Alternatif olarak, bekleyebileceğiniz maksimum düzey sayısını biliyorsanız, bir dizi ve dizini buna bildirebilirsiniz. Muhtemelen bu diziyi statik veya sabit yapmak istersiniz.

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

2

Adam'ın cevabı hakkında yorum yapmak için yeterli temsilcim yok, ama bunu yapmanın en iyi yolu şudur:

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'ın sıfırdan büyük olup olmadığını kontrol etmelisiniz, aksi takdirde bir istisna alırsınız.


2

Bu çözümü görmedim. Şu anda yazılım geliştirmede olduğum yer için daha basit buluyorum:

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

2

Genel kullanım için, StringBuilder sınıfını içeren çözümler çok karakterli dizeleri yinelemek için en iyisidir. Çok sayıda dizenin birleşimini, basit birleştirme işleminin yapamayacağı ve elle daha verimli bir şekilde yapılması zor veya imkansız olacak şekilde işlemek için optimize edilmiştir. Burada gösterilen StringBuilder çözümleri tamamlamak için O (N) yinelemelerini kullanır, bu yineleme sayısı ile orantılı sabit bir orandır.

Bununla birlikte, çok sayıda tekrar için veya yüksek verimlilik seviyelerinin ortadan kaldırılması gerektiğinde, StringBuilder'in temel işlevine benzer bir şey yapmak, ancak orijinal dizeden ziyade hedeften ek kopyalar üretmek, aşağıda olduğu gibi.

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

Bu, her bir yinelemeyle kaynak / hedef dizginin uzunluğunu iki katına çıkarır, bu da orijinal dizeden her geçtiğinde sıfırlama sayaçlarının yükünü azaltır, bunun yerine modern işlemcilerin yapabileceği çok daha uzun dizeyi düzgün bir şekilde okumak ve kopyalamak daha verimli.

Dizenin uzunluğunu iki katına çıkarmak için kaç kez ihtiyaç duyduğunu bulmak için bir base-2 logaritması kullanır ve bunu birçok kez yapmaya devam eder. Kopyalanacak geri kalan artık kopyalandığı toplam uzunluktan daha az olduğundan, daha önce oluşturduğu şeyin bir alt kümesini kopyalayabilir.

StringBuilder içeriğinin bir kopyasını kendi içine her yineleme ile bu içerik ile yeni bir dize üreten yükü olurdu gibi StringBuilder kullanımı üzerinde Array.Copy () yöntemini kullandım. Array.Copy () bunu önler ve yine de son derece yüksek bir verimlilik oranıyla çalışır.

Bu çözüm , tekrar sayısı ile logaritmik olarak artan bir oran olan O (1 + log N) iterasyonlarını alır ( tekrarlama sayısının iki katına çıkarılması bir ek iterasyona eşittir), orantılı olarak artan diğer yöntemlere göre önemli bir tasarruf.


1

Diğer bir yaklaşım dikkate almaktır stringolarak IEnumerable<char>ve belirtilen faktör tarafından bir koleksiyon öğeleri çarpacaktır genel uzantısı yöntemi vardır.

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

Yani sizin durumunuzda:

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

1

Tekrarlı bir çizgi yazdırın.

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

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


Dizenin sonuna \ n eklemek yerine neden Console.WriteLine kullanmadınız?
InxaneNinja

-1

Bunu yapmak için bir ExtensionMethod oluşturabilirsiniz!

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

Veya @ Dan Tao çözümü kullanarak:

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
Kyle'ın cevabı
Thomas Levesque
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.