Existe uma maneira fácil de retornar uma sequência X repetida várias vezes?


318

Estou tentando inserir um certo número de indentações antes de uma seqüência de caracteres com base na profundidade de itens e estou me perguntando se existe uma maneira de retornar uma sequência de vezes X repetidas. Exemplo:

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
Não tenho certeza se isso é aplicável, mas convém consultar também o IndentedTextWriter .
Chris Shouts

Deseja considerar alterar a resposta aceita, pois sua pergunta é sobre repetição de strings e não caracteres que a resposta aceita representa? Até hoje na .Net v4.7, qualquer construtor sobrecarregado da Stringclasse não está usando uma string como parâmetro para criar repetições, o que poderia ter feito da resposta aceita um ajuste perfeito.
RBT 02/09

Respostas:


538

Se você deseja repetir apenas o mesmo caractere, pode usar o construtor de cadeias que aceita um caractere e o número de vezes para repeti-lonew String(char c, int count) .

Por exemplo, para repetir um traço cinco vezes:

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

8
Obrigado por me lembrar sobre esse pequeno recurso interessante. Eu acho que estava perdido no canto da minha mente.
Chris Shouts

3
Como o metacaractere da guia '\ t' é um caractere, isso também funciona para indentação.
Cori

91
Esse é um truque muito útil do C #, mas o título da pergunta é sobre uma string (não um caractere). As outras respostas abaixo desta estão respondendo à pergunta, mas são classificadas muito mais baixas. Não estou tentando desrespeitar a resposta de Ahmad, mas acho que o título dessa pergunta deve ser alterado (se a pergunta é realmente sobre personagens) ou as outras respostas realmente devem ser votadas (e não esta).
Pgprogrammer4

14
@ Ahmad Como eu disse, não diminuí a votação porque acho que você não forneceu boas informações, mas porque as informações que você forneceu não são relevantes para responder à pergunta, conforme indicado atualmente . Imagine se você estivesse procurando uma maneira de repetir seqüências de caracteres (não caracteres) e, quando chegar à pergunta relevante, precisará rolar várias dicas 'úteis' para obter as respostas reais à pergunta. Se o voto negativo for ofensivo para você, eu posso removê-lo. Mas você vê de onde eu estou vindo? Estou totalmente fora da base aqui?
pghprogrammer4

11
@ pghprogrammer4 IMHO, votos negativos podem ser bastante desencorajadores e devem ser usados ​​apenas para respostas prejudiciais / enganosas / verdadeiramente_bad, que precisam de uma mudança importante ou devem ser excluídas pelo autor. As respostas existem porque as pessoas são generosas e desejam compartilhar o que sabem. Geralmente, esses sites realmente incentivam os upvotes a trazer respostas úteis para o topo.
Home

271

Se você estiver usando o .NET 4.0, poderá usar string.Concatjunto com Enumerable.Repeat.

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

Caso contrário, eu usaria algo como a resposta de Adam .

A razão pela qual geralmente não aconselho usar a resposta de Andrey é simplesmente que a ToArray()ligação introduz uma sobrecarga supérflua que é evitada com a StringBuilderabordagem sugerida por Adam. Dito isto, pelo menos funciona sem a necessidade do .NET 4.0; e é rápido e fácil (e não vai te matar se a eficiência não for muito preocupante).


3
Isso funcionará bem com strings - como se você precisasse concaturar espaços sem quebra (& nbsp;). Mas eu assumo o construtor seqüência será mais rápida se você está lidando com um char ...
woodbase

1
O construtor de strings funcionará apenas para um caractere. a opção de usar Concat Repita irá trabalhar para cordas
Eli Dagan

4
Obrigado por responder à pergunta do título real!
Mark K Cowan

Eu gosto desta resposta!
Ji_in_coding

Se você estiver obtendo -System.Linq.Enumerable etc como saída, é porque você (ok, era eu) foi muito rápido para copiar / colar e modificar o trecho. Se você precisar concatenar uma string diferente na saída do Enumerable.Repeat, precisará fazer o seguinte: 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
É melhor passar input.Length * countpara o StringBuilderconstrutor, não acha?
Dan Tao

E se você passar input.Length * countpara o StringBuilderconstrutor, poderá pular StringBuildercompletamente e criar um caractere [] do tamanho certo imediatamente.
dtb 20/09/10

@ dtb: Estou curioso para saber por que você faria isso. Não consigo imaginar que seria muito (se houver) mais rápido do que usar StringBuilderae o código seria menos transparente.
Adam Robinson

@dtb: eca. Use StringBuilder; foi projetado para fazer isso com eficiência.
Home

Adam, vejo que você teve o trabalho de testar a entrada de nulo ao adicioná-lo à contagem, mas permitiu que isso acontecesse, contando com o Append para não fazer nada, dado o nulo. IMHO isso é menos que óbvio: se nulo é uma entrada possível, por que não testar isso antecipadamente e retornar uma string vazia?
precisa

46

solução de melhor desempenho para string

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

1
Claro que é uma linha única. Claro que não é o melhor desempenho. O StringBuilder tem um pouco de sobrecarga com um número muito pequeno de concatenações.
26418 Teejay

25

Para muitos cenários, esta é provavelmente a solução mais limpa:

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

O uso é então:

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

Isso se baseia em outras respostas (principalmente no @ c0rd). Além da simplicidade, possui os seguintes recursos, que nem todas as outras técnicas discutidas compartilham:

  • Repetição de uma sequência de qualquer tamanho, não apenas de um caractere (conforme solicitado pelo OP).
  • Uso eficiente de StringBuilderpré-alocação de armazenamento.

Eu concordo, isso é maravilhoso. Acabei de adicionar isso ao meu código com crédito para Bob! Obrigado!
John Burley

22

Use String.PadLeft , se a sequência desejada contiver apenas um único caractere.

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

Crédito devido aqui


1
BEIJO. Para o que precisamos, StringBuilders e extensões, afinal? Este é um problema muito simples.
DOK 20/09

1
Não sei, isso parece resolver o problema com uma certa elegância. E aliás, quem você está chamando de 'S'? :-)
Steve Townsend

7
Mas não é este realmente apenas uma versão menos óbvia do stringconstrutor que recebe um chare umint ?
Dan Tao

Ele ficaria melhor com String.Empty em vez de "" ... caso contrário, boa solução;)
Thomas Levesque

@ Steve BTW os String.PadLeftargumentos são revertidos. O countvem antes do caractere de preenchimento.
Ahmad Mageed 20/09/10

17

Você pode repetir sua string (caso não seja um único caractere) e concatinar o resultado, assim:

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

12

Gostaria de ir para a resposta de Dan Tao, mas se você não estiver usando o .NET 4.0, poderá fazer algo assim:

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

3
Se eu vou findthis trecho no meu projeto, eu vou saber quem está violando o beijo e porquê ... agradável de outra forma
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: Eu acho que você precisaria fazer isso antes do .NET 4.0.
Dan Tao

3

Eu gosto da resposta dada. Na mesma linha, porém, é o que eu usei no passado:

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

Isso criará um recuo, mas tecnicamente a questão era repetir uma string. Se o recuo da string for algo como> - <, isso e a resposta aceita não funcionariam. Nesse caso, a solução da c0rd usando stringbuilder parece boa, embora a sobrecarga do stringbuilder possa de fato não torná-lo o mais eficiente. Uma opção é criar uma matriz de cadeias, preenchê-la com cadeias de indentação e concatenar isso. Para whit:

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

Todos nós gostamos de uma solução inteligente, mas às vezes simples é melhor.


3

Adicionando o método de extensão que estou usando em todos os meus projetos:

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

Espero que alguém possa usá-lo ...


2

Surpreso, ninguém foi à moda antiga. Não estou fazendo nenhuma reivindicação sobre esse código, mas apenas por diversão:

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

Obviamente deixei isso como um teste para o leitor astuto :) Muito astuto, obrigado.
OlduwanSteve

1
Não há nada old-school sobre a nomeação de um parâmetro @this, que sempre foi uma idéia horrível :)
Dave Jellison

2
Prefiro não usar a palavra-chave como nomes de variáveis, a menos que seja forçado (como @class = "something" ao trabalhar com o MVC, porque você precisa de uma classe referente ao CSS, mas é uma palavra-chave em C # (é claro). Embora seja certamente um código compilável e legítimo, pense também na facilidade de leitura e digitação, ambos os motivos práticos. Sou muito parcial ao usar nomes de parâmetros para descrever o que está acontecendo primeiro, então Repita (esta semente de sequência, int count) pode ser imho mais ilustrativo.
Dave Jellison

1
Interessante, obrigado. Acho que, nesse caso, você provavelmente está certo de que isso é menos descritivo do que poderia ser. No entanto, em geral, acho bastante comum estender classes ou interfaces em que o único nome razoável para 'this' é bastante genérico. Se você tiver um parâmetro chamado 'instance' ou 'it' ou 'str' (consulte MSDN ), o que você provavelmente quis dizer é 'this'.
precisa saber é o seguinte

1
Eu basicamente concordo com você, mas por uma questão de argumento ... desde que eu sei mais sobre a string que estou construindo, posso fazer um trabalho marginalmente melhor que o StringBuilder. O StringBuilder precisa adivinhar quando está alocando memória. Concedido é improvável que importa em muitos cenários, mas alguém lá fora, pode ser a construção de um enorme número de cordas utilizadas na paralela e ser feliz para ganhar alguns ciclos de clock volta :)
OlduwanSteve

2

Não tenho certeza de como isso funcionaria, mas é um código fácil. (Eu provavelmente fiz parecer mais complicado do que é.)

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

Como alternativa, se você souber o número máximo de níveis que pode esperar, basta declarar uma matriz e indexá-la. Você provavelmente desejaria tornar essa matriz estática ou constante.

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

2

Não tenho representante suficiente para comentar a resposta de Adam, mas a melhor maneira de fazê-lo é:

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

Você deve verificar se numTimes é maior que zero, caso contrário, você receberá uma exceção.


2

Eu não vi essa solução. Acho mais simples para onde atualmente estou no desenvolvimento de software:

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

2

Para uso geral, as soluções que envolvem a classe StringBuilder são melhores para repetir seqüências de vários caracteres. É otimizado para lidar com a combinação de um grande número de strings de uma maneira que a concatenação simples não pode e que seria difícil ou impossível fazer com mais eficiência manualmente. As soluções StringBuilder mostradas aqui usam iterações O (N) para concluir, uma taxa fixa proporcional ao número de vezes que é repetida.

No entanto, para um número muito grande de repetições ou onde altos níveis de eficiência devem ser extraídos, uma abordagem melhor é fazer algo semelhante à funcionalidade básica do StringBuilder, mas produzir cópias adicionais a partir do destino, e não da sequência original, como abaixo.

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

Isso dobra o comprimento da cadeia de origem / destino a cada iteração, o que economiza a sobrecarga de redefinir os contadores toda vez que ela passa pela cadeia original, em vez de ler e copiar facilmente a cadeia agora muito mais longa, algo que os processadores modernos podem fazer mais eficientemente.

Ele usa um logaritmo de base 2 para descobrir quantas vezes ele precisa dobrar o comprimento da string e, em seguida, continua fazendo isso várias vezes. Como o restante a ser copiado agora é menor que o tamanho total do qual está copiando, ele pode simplesmente copiar um subconjunto do que já gerou.

Eu usei o método Array.Copy () sobre o uso de StringBuilder, pois uma cópia do conteúdo do StringBuilder em si teria a sobrecarga de produzir uma nova string com esse conteúdo a cada iteração. Array.Copy () evita isso, enquanto ainda opera com uma taxa extremamente alta de eficiência.

Essa solução leva para que as iterações O (1 + log N) sejam concluídas, uma taxa que aumenta logaritmicamente com o número de repetições (dobrar o número de repetições equivale a uma iteração adicional), uma economia substancial em relação aos outros métodos, que aumentam proporcionalmente.


1

Outra abordagem é considerar stringcomo IEnumerable<char>e ter um método de extensão genérico que multiplicará os itens de uma coleção pelo fator especificado.

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

Então, no seu caso:

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

1

Imprima uma linha com repetição.

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

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


Por que você não usou o Console.WriteLine em vez de anexar \ n no final da string?
InxaneNinja

-1

Você pode criar um ExtensionMethod para fazer isso!

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

Ou usando a solução @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
Veja meu comentário na resposta de Kyle
Thomas Levesque
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.