¿Cómo repetir una cadena un número variable de veces en C ++?

127

Quiero insertar espacios 'n' (o cualquier cadena) al comienzo de una cadena en C ++. ¿Hay alguna forma directa de hacer esto usando std :: strings o char * strings?

Por ejemplo, en Python simplemente podrías hacer

>>> "." * 5 + "lolcat"
'.....lolcat'

fuente
¿Alguien proporciona una respuesta usando QString?
Akiva

Respuestas:

175

En el caso particular de repetir un solo carácter, puede usar std::string(size_type count, CharT ch):

std::string(5, '.') + "lolcat"

NÓTESE BIEN. Esto no se puede usar para repetir cadenas de caracteres múltiples.

luke
fuente
79
El OP solicitó repetir una cadena, no un carácter.
Florian Kaufmann
39

No hay una forma idiomática directa de repetir cadenas en C ++ equivalentes al operador * en Python o al operador x en Perl. Si está repitiendo un solo carácter, el constructor de dos argumentos (como lo sugirieron las respuestas anteriores) funciona bien:

std::string(5, '.')

Este es un ejemplo artificial de cómo puede usar un ostringstream para repetir una cadena n veces:

#include <sstream>

std::string repeat(int n) {
    std::ostringstream os;
    for(int i = 0; i < n; i++)
        os << "repeat";
    return os.str();
}

Dependiendo de la implementación, esto puede ser un poco más eficiente que simplemente concatenar la cadena n veces.

El comodoro Jaeger
fuente
17

Use una de las formas de string :: insert:

std::string str("lolcat");
str.insert(0, 5, '.');

Esto insertará "....." (cinco puntos) al comienzo de la cadena (posición 0).

camh
fuente
15
El OP solicitó repetir una cadena, no un carácter.
Brent
@Brent El OP solicitó ambos: "'n' espacios (o cualquier cadena)", y luego continúa demostrando su intención con un solo punto como cadena. El inglés no es el idioma principal de muchas personas, por lo que a veces es necesario adivinar sus requisitos exactos y, cuando se analiza, la pregunta realmente es cómo hacer esto con un solo carácter. Lamento que haya encontrado mi respuesta lo suficientemente inútil como para desestimarla.
camh
13

Sé que esta es una vieja pregunta, pero estaba buscando hacer lo mismo y he encontrado lo que creo que es una solución más simple. Parece que cout tiene esta función incorporada con cout.fill (), vea el enlace para obtener una explicación 'completa'

http://www.java-samples.com/showtutorial.php?tutorialid=458

cout.width(11);
cout.fill('.');
cout << "lolcat" << endl;

salidas

.....lolcat
Ian
fuente
66
solo puntos: cambie la última línea a ...cout << "" << endl;
musefan
9

A los efectos del ejemplo proporcionado por la OP std :: ctor de cadena es suficiente: std::string(5, '.'). Sin embargo, si alguien está buscando una función para repetir std :: string varias veces:

std::string repeat(const std::string& input, unsigned num)
{
    std::string ret;
    ret.reserve(input.size() * num);
    while (num--)
        ret += input;
    return ret;
}
Pavel P
fuente
8

Como aludió el comodoro Jaeger, no creo que ninguna de las otras respuestas realmente responda esta pregunta; la pregunta pregunta cómo repetir una cadena, no un carácter.

Si bien la respuesta dada por Commodore es correcta, es bastante ineficiente. Aquí hay una implementación más rápida, la idea es minimizar las operaciones de copia y las asignaciones de memoria haciendo crecer primero la cadena exponencialmente:

#include <string>
#include <cstddef>

std::string repeat(std::string str, const std::size_t n)
{
    if (n == 0) {
        str.clear();
        str.shrink_to_fit();
        return str;
    } else if (n == 1 || str.empty()) {
        return str;
    }
    const auto period = str.size();
    if (period == 1) {
        str.append(n - 1, str.front());
        return str;
    }
    str.reserve(period * n);
    std::size_t m {2};
    for (; m < n; m *= 2) str += str;
    str.append(str.c_str(), (n - (m / 2)) * period);
    return str;
}

También podemos definir un operator*para acercarnos a la versión de Python:

#include <utility>

std::string operator*(std::string str, std::size_t n)
{
    return repeat(std::move(str), n);
}

En mi máquina, esto es aproximadamente 10 veces más rápido que la implementación dada por Commodore, y aproximadamente 2 veces más rápido que una solución ingenua de 'agregar n - 1 veces' .

Daniel
fuente
Su implementación no 'minimiza la copia'. Tenga en cuenta que +=dentro de su bucle for también tiene un bucle de algún tipo que realiza str.size()iteraciones. str.size()crece en cada iteración del bucle externo, por lo que después de cada iteración externa, el bucle interno tiene que hacer más iteraciones. Tu y la ingenua implementación de 'copiar n veces' en total ambos n * periodcaracteres de copia . Su implementación solo asigna una memoria debido a la inicial reserve. Supongo que perfilaste tu implementación con una pequeña stry una grande n, pero no también con una grande stry una pequeña n.
Florian Kaufmann
@FlorianKaufmann No estoy seguro de por qué elegiste atacar mi respuesta. Pero por "minimizar la copia" me refiero a 'operaciones de copia'. La idea es que copiar una pequeña cantidad de bloques grandes es más eficiente (por una variedad de razones) que copiar una gran cantidad de bloques pequeños. Potencialmente evito una asignación adicional en la cadena de entrada sobre el método ingenuo.
Daniel
2
Fue un comentario que decía que no creo su afirmación de que su solución es mucho mejor en términos de eficiencia que la solución ingenua. En mis mediciones, en comparación con la solución ingenua, su código es más rápido con cadenas pequeñas y muchas repeticiones, pero más lento con cadenas largas y pocas repeticiones. ¿Podría proporcionar enlaces que expliquen con más detalle la variedad de razones por las cuales copiar algunos bloques grandes tiene un mayor rendimiento que copiar muchos bloques pequeños? Puedo pensar en la predicción de ramas. Con respecto al caché de la CPU, no estoy seguro de qué variante se prefiere.
Florian Kaufmann
@FlorianKaufmann No veo diferencias significativas para grandes stry pequeños nentre los dos enfoques. Creo que esto tiene más que ver con la tubería general que con la predicción de rama en sí, también hay problemas de alineación de datos a considerar. Debería hacer una nueva pregunta para los detalles de por qué esto es más amigable con el procesador / memoria, estoy seguro de que ganaría mucho interés y recibiría una mejor respuesta de la que puedo dar aquí.
Daniel
1
@FlorianKaufmann: en x86, rep movsbes una de las formas más eficientes de copiar, al menos para copias medianas a grandes. Su implementación microcodificada tiene una sobrecarga de inicio casi constante (tanto en AMD como en Intel), por ejemplo, en Sandybridge, ~ 15 a 40 ciclos, más 4 ciclos por línea de caché de 64B (el mejor de los casos) . Para copias pequeñas, un bucle SSE es mejor porque no tiene la sobrecarga de inicio. Pero luego está sujeto a predicciones erróneas de la rama.
Peter Cordes
5

ITNOA

Puede usar la función C ++ para hacer esto.

 std::string repeat(const std::string& input, size_t num)
 {
    std::ostringstream os;
    std::fill_n(std::ostream_iterator<std::string>(os), num, input);
    return os.str();
 }
sorosh_sabz
fuente
1
¿Qué significa "ITNOA"? No puedo encontrar ninguna referencia en línea.
Folling