Posso "multiplicar" uma string (em C #)?

136

Suponha que eu tenha uma string, por exemplo,

string snip =  "</li></ul>";

Quero basicamente escrever várias vezes, dependendo de algum valor inteiro.

string snip =  "</li></ul>";
int multiplier = 2;

// TODO: magic code to do this 
// snip * multiplier = "</li></ul></li></ul>";

Edição: Eu sei que posso facilmente escrever minha própria função para implementar isso, eu só estava me perguntando se havia algum operador de string estranho que eu não sabia sobre

Apesar
fonte

Respostas:

222

No .NET 4, você pode fazer isso:

String.Concat(Enumerable.Repeat("Hello", 4))
Will Dean
fonte
9
E não é muito mais em .NET 3.5: String.Concat (Enumerable.Repeat ( "Olá", 4) .ToArray ())
Mark Foreman
4
Eu não acho nada elegante. Em python, o código para fazer isso é: snip * multiplicador (não é horrível .. mas também não é bonito).
ouriço demente Dem
7
@dementedhedgehog Eu sei que você reconhece sua própria demência, mas mesmo sofrendo, você pode ver que um exemplo em Python não teria sido uma resposta muito boa para uma pergunta em C # ... Eu acho que a maioria de nós pode ver que a seleção de idiomas é sempre um compromisso, e praticamente qualquer resposta a qualquer pergunta pode ser mencionada na advocacia, a favor ou contra.
Will Dean
1
@ Dean Dean: Acabei de me referir à declaração de Matthew Nichols sobre a elegância do código que você tem lá. Tanto quanto posso dizer, seu código é elegante para C #, como está atualmente, para esse problema. O ponto que estou tentando enfatizar é: (acredito) seria muito fácil (para a Microsoft, como as strings são seladas) estender o C # para sobrecarregar o operador * para permitir a multiplicação int * da string. O que seria realmente elegante em algum sentido universal, não apenas elegante dentro do contexto das restrições impostas pelo C #, como existe no momento.
ouriço demente Dem
@dementedhedgehog Para mim, isso vai contra o que o binário operator*representa. Para cada um deles, eu acho.
TEK 15/01
99

Observe que se sua "string" é apenas um caractere, há uma sobrecarga do construtor de string para lidar com isso:

int multipler = 10;
string TenAs = new string ('A', multipler);
James Curran
fonte
Isso é realmente profissional.
Zaven Zareyan 29/11/19
61

Infelizmente / felizmente, a classe string é selada para que você não possa herdar dela e sobrecarregar o operador *. Você pode criar um método de extensão:

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);
Tamas Czinege
fonte
5
Apenas para onde eu estava indo! Você provavelmente poderia otimizar usando o multiplicador source.Length * no ctor StringBuilder
Marc Gravell
2
O comentário de Marc foi exatamente onde eu estava indo :)
Jon Skeet
1
Você precisa (source.Length * multiplicador), não apenas (multiplicador)
Marc Gravell
1
Muita certeza. Ele aloca uma string (desse comprimento) nos bastidores e depois a modifica. Não funciona como uma lista normal <T> etc.
Marc Gravell
1
O método de extensão é ideal aqui.
22710 Chris Ballance
12

Estou com o DrJokepu , mas se, por algum motivo, você quiser trapacear usando a funcionalidade incorporada, poderá fazer algo assim:

string snip = "</li></ul>";
int multiplier = 2;

string result = string.Join(snip, new string[multiplier + 1]);

Ou, se você estiver usando o .NET 4:

string result = string.Concat(Enumerable.Repeat(snip, multiplier));

Pessoalmente, eu não me incomodaria - um método de extensão personalizado é muito melhor.

LukeH
fonte
1
Eu nunca soube que havia uma trapaça como esta. =)
Jronny 21/07
10

Apenas por uma questão de integridade - aqui está outra maneira de fazer isso:

public static string Repeat(this string s, int count)
{
    var _s = new System.Text.StringBuilder().Insert(0, s, count).ToString();
    return _s;
}

Acho que peguei esse no Stack Overflow há algum tempo, então não é minha ideia.

user51710
fonte
9

Você precisaria escrever um método - é claro, com o C # 3.0, poderia ser um método de extensão:

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

então:

string bar = "abc";
string foo = bar.Repeat(2);
Marc Gravell
fonte
Nem o .NET3 tinha Enumerable.Repeat?
Will Dean
@ Will - .NET 3 foi WCF / WPF etc, então não; ele existe no .NET 3.5, mas você precisaria string.Jointambém - por que não apenas repetir n vezes? Muito mais direto.
Marc Gravell
Obrigado - eu não estava pensando corretamente sobre 3.0 vs 3.5. Por que não usar apenas um loop, certamente essa é toda a essência do debate funcional versus imperativo? Eu publiquei uma resposta .net 4 abaixo, que eu acho que não é tão ruim no debate 'esperteza funcional' vs 'óbvio em loop'.
Will Dean
8

Um pouco tarde (e apenas por diversão), se você realmente quiser usar o *operador para este trabalho, poderá fazer o seguinte:

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

E entao:

var newStr = new StringWrap("TO_REPEAT") * 5;

Note-se que, desde que você é capaz de encontrar um comportamento razoável para eles, você também pode lidar com outros operadores através de StringWrapclasse, como \, ^, %etc ...

PS:

Multiply()créditos de extensão para @DrJokepu todos os direitos reservados ;-)

digEmAll
fonte
7

Isso é muito mais conciso:

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

O espaço using System.Text;para nome deve ser importado nesse caso.

user734119
fonte
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();
}
Chris Ballance
fonte
2

Se você possui .Net 3.5, mas não 4.0, pode usar o System.Linq

String.Concat(Enumerable.Range(0, 4).Select(_ => "Hello").ToArray())
Frank Schwieterman
fonte
Para obter uma única string que você ainda precisa String.Concat, e na versão 3.5 também .ToArray(). Receio que não seja a solução mais elegante.
Kobi
2

Como todo mundo está adicionando seus próprios exemplos do .NET4 / Linq, é melhor adicionar os meus. (Basicamente, DrJokepu's, reduzido a uma linha)

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();
}
James Curran
fonte
0

Ok, aqui está minha opinião sobre o assunto:

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

Estou sendo um pouco bobo, é claro, mas quando preciso ter tabulação nas classes geradoras de código, o Enumerable.Repeat faz isso por mim. E sim, a versão StringBuilder também está bem.

Dmitri Nesteruk
fonte
0

Aqui está minha opinião sobre isso apenas para referência futura:

    /// <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
fonte
Espero que você realmente não use ParallelEnumerableem situações como esta. string.Joinprecisa usar os elementos em ordem; portanto, paralelizar sua geração é desnecessário.
Gabe
@ Gabe: Como os itens são os mesmos e são realmente apenas cópias desta string, não há necessidade de se preocupar com o pedido aqui, eu acho.
Jronny
Eu concordo com muitos professores da área que geralmente é bom codificar para dizer o que você quer dizer. Não há nenhum benefício para dizer que você quer esse paralelo e só secretamente pensar "Bem, eu sei que não vai ser paralelo, neste caso particular de qualquer maneira"
sehe
Eu acho que torná-lo paralelo torna mais rápido, ou por favor, me esclareça. É por isso que estou convertendo isso para ParallelEnumerable, portanto, acho que codifico para dizer o que quero dizer ... Obrigado.
Jronny