¿Hay una manera fácil de devolver una cadena repetida X veces?

318

Estoy tratando de insertar un cierto número de hendiduras antes de una cadena basada en la profundidad de un elemento y me pregunto si hay una manera de devolver una cadena repetida X veces. Ejemplo:

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 "---------".
Abe Miessler
fuente
99
No estoy seguro de si esto es aplicable, pero es posible que desee ver también IndentedTextWriter .
Chris grita el
¿Desea considerar cambiar la respuesta aceptada ya que su pregunta es sobre la repetición de cadenas y no los caracteres que representa la respuesta aceptada? Desde hoy hasta .Net v4.7, cualquier constructor de Stringclase sobrecargado no está tomando una cadena como parámetro para crear repeticiones, lo que podría haber hecho que la respuesta aceptada encajara perfectamente.
RBT

Respuestas:

538

Si solo tiene la intención de repetir el mismo carácter, puede usar el constructor de cadenas que acepta un carácter y la cantidad de veces que lo repite new String(char c, int count).

Por ejemplo, para repetir un guión cinco veces:

string result = new String('-', 5);
Output: -----
Ahmad Mageed
fuente
8
Gracias por recordarme esta pequeña característica agradable. Creo que se perdió en el rincón de mi mente.
Chris grita el
3
Dado que el metacarácter de pestaña '\ t' es un carácter, esto también funciona para la sangría.
cori
9191
Este es un truco muy útil de C #, pero el título de la pregunta es sobre una cadena (no un carácter). Las otras respuestas debajo de esta en realidad están respondiendo la pregunta, pero tienen una calificación mucho más baja. No estoy tratando de faltarle el respeto a la respuesta de Ahmad, pero creo que el título de esta pregunta debería cambiarse (si la pregunta realmente se refiere a los personajes) o las otras respuestas realmente deberían ser votadas (y no esta).
pghprogrammer4
14
@ Ahmad Como dije, no voté en contra porque no creo que hayas proporcionado buena información, sino porque la información que proporcionaste no es relevante para responder la pregunta como se indica actualmente . Imagínese si estaba buscando una manera de repetir cadenas (no caracteres) y cuando llega a la pregunta relevante tiene que desplazarse por varios consejos 'útiles' para obtener las respuestas reales a la pregunta. Si el voto negativo es ofensivo para usted, puedo eliminarlo. ¿Pero ves de dónde vengo? ¿Estoy totalmente fuera de base aquí?
pghprogrammer4
11
@ pghprogrammer4 En mi humilde opinión, los votos negativos pueden ser bastante desalentadores, y solo se deben usar para respuestas dañinas / engañosas / verdaderamente falsas, que necesitan un cambio importante o deben ser eliminadas por su autor. Las respuestas existen porque las personas son generosas y desean compartir lo que saben. En general, estos sitios realmente fomentan los votos a favor para traer respuestas útiles a la cima.
ToolmakerSteve
271

Si usa .NET 4.0, puede usarlo string.Concatjunto con Enumerable.Repeat.

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

De lo contrario, iría con algo como la respuesta de Adam .

La razón por la que generalmente no recomendaría usar la respuesta de Andrey es simplemente que la ToArray()llamada introduce una sobrecarga superflua que se evita con el StringBuilderenfoque sugerido por Adam. Dicho esto, al menos funciona sin requerir .NET 4.0; y es rápido y fácil (y no te va a matar si la eficiencia no es una gran preocupación).

Dan Tao
fuente
3
Esto funcionará bien con cadenas, como si necesita concatenar espacios que no se rompen (& nbsp;). Pero supongo que el constructor de cadenas será más rápido si se trata de un
personaje
1
El constructor de cadenas solo funcionará para un carácter. a la opción de usar Concat Repeat funcionará para cadenas
Eli Dagan
44
¡Gracias por responder la pregunta del título real!
Mark K Cowan
Me gusta esta respuesta!
Ji_in_coding
Si está obteniendo -System.Linq.Enumerable, etc. como salida, es porque usted (ok, este era yo) fue demasiado rápido para copiar / pegar y modificar el fragmento. Si necesita concatenar una cadena diferente en la salida del Enumerable. Repita, debe hacerlo así: 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;
    }
}
Adam Robinson
fuente
66
Bien podría pasar input.Length * countal StringBuilderconstructor, ¿no te parece?
Dan Tao
Y si pasa input.Length * countal StringBuilderconstructor, puede omitir el StringBuilderconjunto y crear un carácter [] del tamaño correcto de inmediato.
dtb
@dtb: Tengo curiosidad por qué querrías hacer eso. No puedo imaginar que sería mucho (si lo hubiera) más rápido que usar a StringBuilder, y el código sería menos transparente.
Adam Robinson
@dtb: qué asco. Use StringBuilder; Fue diseñado para hacer esto de manera eficiente.
ToolmakerSteve
Adam, veo que te tomaste la molestia de probar la entrada para nulo al agregarla para contar, pero luego permitiste que eso no funcionara, confiando en Append para no hacer nada dado el nulo. En mi humilde opinión esto es menos que obvio: si nulo es una entrada posible, ¿por qué no probar eso por adelantado y devolver una cadena vacía?
ToolmakerSteve
46

solución más eficaz para cadena

string result = new StringBuilder().Insert(0, "---", 5).ToString();
c0rd
fuente
1
Claro que es una bonita frase. Claro que no es el más eficiente. StringBuilder tiene un poco de sobrecarga en muy pocas concatenaciones.
Teejay
25

Para muchos escenarios, esta es probablemente la mejor solución:

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

El uso es entonces:

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

Esto se basa en otras respuestas (particularmente @ c0rd's). Además de la simplicidad, tiene las siguientes características, que no todas las otras técnicas discutidas comparten:

  • Repetición de una cadena de cualquier longitud, no solo un carácter (según lo solicitado por el OP).
  • Uso eficiente de StringBuilderla preasignación a través del almacenamiento.
Bob Sammers
fuente
Estoy de acuerdo, esto es maravilloso. ¡Acabo de agregar esto a mi código con crédito a Bob! ¡Gracias!
John Burley el
22

Use String.PadLeft , si su cadena deseada contiene solo un carácter.

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

Crédito adeudado aquí

Steve Townsend
fuente
1
BESO. ¿Para qué necesitamos StringBuilders y extensiones? Este es un problema muy simple.
DOK
1
No sé, esto parece resolver el problema según lo expresado con cierta elegancia. Y por cierto, ¿a quién llamas 'S'? :-)
Steve Townsend
77
Pero no es esto en realidad una versión menos obvia de la stringconstructor que toma una chary unint ?
Dan Tao
Se vería mejor con String.Empty en lugar de "" ... de lo contrario, es una buena solución;)
Thomas Levesque
@Steve BTW los String.PadLeftargumentos se invierten. El countviene antes del carácter de relleno.
Ahmad Mageed
17

Puede repetir su cadena (en caso de que no sea un solo carácter) y concat el resultado, de esta manera:

String.Concat(Enumerable.Repeat("---", 5))
LiRoN
fuente
12

Buscaría la respuesta de Dan Tao, pero si no estás usando .NET 4.0 puedes hacer algo así:

public static string Repeat(this string str, int count)
{
    return Enumerable.Repeat(str, count)
                     .Aggregate(
                        new StringBuilder(),
                        (sb, s) => sb.Append(s))
                     .ToString();
}
Thomas Levesque
fuente
3
Si encuentro este fragmento en mi proyecto, me pregunto quién ha estado violando el KISS y por qué ... bueno, de lo contrario
Noctis es el
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());
Andrey
fuente
1
@ Adam: Creo que deberías hacer eso antes de .NET 4.0.
Dan Tao
3

Me gusta la respuesta dada. Sin embargo, a lo largo de las líneas de sames es lo que he usado en el pasado:

"". PadLeft (3 * Sangría, '-')

Esto cumplirá creando una sangría, pero técnicamente la pregunta era repetir una cadena. Si la sangría de la cadena es algo así como> - <entonces esto, así como la respuesta aceptada, no funcionaría. En este caso, la solución de c0rd usando stringbuilder se ve bien, aunque la sobrecarga de stringbuilder puede no ser la más eficiente. Una opción es construir una matriz de cadenas, llenarla con cadenas de sangría y luego concatenarla. A 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 amamos una solución inteligente, pero a veces lo mejor es simple

Londo Mollari
fuente
3

Agregar el método de extensión que estoy usando en todos mis proyectos:

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

Espero que alguien pueda usarlo ...

Mayer Spitzer
fuente
2

Sorprendido, nadie fue a la vieja escuela. No estoy haciendo ninguna afirmación sobre este código, pero solo por diversión:

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
fuente
Obviamente lo dejé como una prueba para el lector astuto :) Muy astuto, gracias.
OlduwanSteve
1
No hay nada de antiguo en nombrar un parámetro @esto, siempre ha sido una idea horrible :)
Dave Jellison
2
Prefiero no usar palabras clave como nombres de variables a menos que sea forzado (como @class = "something" cuando trabaje con MVC porque necesita una clase que se refiera a CSS pero es una palabra clave en C # (por supuesto). Esto no es solo yo hablando, es una regla general. Aunque ciertamente es un código compilable y legítimo, piense también en la facilidad de lectura y escritura, ambas razones prácticas. Soy muy parcial al usar nombres de parámetros para describir lo que está sucediendo primero, así que repita (esta secuencia inicial, int count) podría ser más ilustrativa en mi humilde opinión.
David Jellison
1
Interesante, gracias. Creo que en este caso probablemente tengas razón en que esto es menos descriptivo de lo que podría ser. Sin embargo, en general, creo que es bastante común extender las clases o interfaces donde el único nombre razonable para 'esto' es bastante genérico. Si tiene un parámetro llamado 'instancia' o 'it' o 'str' (vea MSDN ), entonces lo que probablemente quiso decir es 'esto'.
OlduwanSteve
1
Básicamente estoy de acuerdo con usted, pero en aras de la discusión ... ya que sé más sobre la cadena que estoy construyendo, puedo hacer un trabajo marginalmente mejor que StringBuilder. StringBuilder tiene que adivinar cuando asigna memoria. De acuerdo, es poco probable que importe en muchos escenarios, pero alguien por ahí podría estar construyendo una enorme cantidad de cadenas repetidas en paralelo y estar contento de recuperar algunos ciclos de reloj :)
OlduwanSteve
2

No estoy seguro de cómo funcionaría esto, pero es un código fácil. (Probablemente lo he hecho parecer más complicado de lo que es).

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

Alternativamente, si conoce la cantidad máxima de niveles que puede esperar, puede declarar una matriz e indexarla. Probablemente quiera hacer esta matriz estática o constante.

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

No tengo suficiente representante para comentar la respuesta de Adam, pero la mejor manera de hacerlo es así:

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

Debe verificar si numTimes es mayor que cero, de lo contrario obtendrá una excepción.

Gru
fuente
2

No vi esta solución. Me resulta más simple para el lugar donde estoy actualmente en el desarrollo de software:

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

Para uso general, las soluciones que involucran la clase StringBuilder son las mejores para repetir cadenas de caracteres múltiples. Está optimizado para manejar la combinación de grandes cantidades de cadenas de una manera que la concatenación simple no puede y eso sería difícil o imposible de hacer de manera más eficiente a mano. Las soluciones de StringBuilder que se muestran aquí usan iteraciones O (N) para completar, una tarifa plana proporcional al número de veces que se repite.

Sin embargo, para un gran número de repeticiones, o donde se deben extraer altos niveles de eficiencia, un mejor enfoque es hacer algo similar a la funcionalidad básica de StringBuilder pero producir copias adicionales desde el destino, en lugar de desde la cadena original, como a continuación.

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

Esto duplica la longitud de la cadena de origen / destino con cada iteración, lo que ahorra la sobrecarga de restablecer contadores cada vez que atraviesa la cadena original, en lugar de leer y copiar sin problemas la cadena ahora mucho más larga, algo que los procesadores modernos pueden hacer mucho más eficientemente

Utiliza un logaritmo de base 2 para encontrar cuántas veces necesita duplicar la longitud de la cadena y luego lo hace tantas veces. Dado que el resto a copiar ahora es menor que la longitud total desde la que está copiando, puede simplemente copiar un subconjunto de lo que ya ha generado.

He usado el método Array.Copy () sobre el uso de StringBuilder, ya que una copia del contenido del StringBuilder en sí mismo tendría la sobrecarga de producir una nueva cadena con ese contenido con cada iteración. Array.Copy () evita esto, mientras sigue funcionando con una tasa de eficiencia extremadamente alta.

Esta solución toma O (1 + log N) iteraciones para completar, una tasa que aumenta logarítmicamente con el número de repeticiones (duplicar el número de repeticiones es igual a una iteración adicional), un ahorro sustancial sobre los otros métodos, que aumentan proporcionalmente.

buchWyrm
fuente
1

Otro enfoque es considerar stringcomo IEnumerable<char>y tienen un método de extensión genérica que multiplicará los elementos de una colección por el factor especificado.

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

Entonces en tu caso:

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

Imprime una línea con repetición.

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

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

Kamal
fuente
¿Por qué no usaste Console.WriteLine en lugar de agregar \ n al final de la cadena?
InxaneNinja
-1

¡Puedes crear un ExtensionMethod para hacer eso!

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

O usando la solución @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))
  }
}
Zote
fuente
3
Ver mi comentario a la respuesta de Kyle
Thomas Levesque