¿Equivalente en C++ de StringBuffer / StringBuilder?


¿Existe una clase de Biblioteca de plantillas estándar de C++ que proporcione una eficiente funcionalidad de concatenación de cadenas, similar a la de C# StringBuilder o la de Java StringBuffer?

Author: An̲̳̳drew, 2010-03-17

10 answers

TENGA en cuenta que esta respuesta ha recibido cierta atención recientemente. No estoy abogando por esto como una solución (es una solución que he visto en el pasado, antes del STL). Es un enfoque interesante y solo debe aplicarse sobre std::string o std::stringstream si después de perfilar su código descubre que esto hace una mejora.

Normalmente utilizo cualquiera std::string o std::stringstream. Nunca he tenido ningún problema con estos. Normalmente me reservaría un poco de espacio primero si conozco el áspero tamaño de la cadena por adelantado.

He visto a otras personas hacer su propio constructor de cadenas optimizado en el pasado distante.

class StringBuilder {
private:
    std::string main;
    std::string scratch;

    const std::string::size_type ScratchSize = 1024;  // or some other arbitrary number

public:
    StringBuilder & append(const std::string & str) {
        scratch.append(str);
        if (scratch.size() > ScratchSize) {
            main.append(scratch);
            scratch.resize(0);
        }
        return *this;
    }

    const std::string & str() {
        if (scratch.size() > 0) {
            main.append(scratch);
            scratch.resize(0);
        }
        return main;
    }
};

Utiliza dos cadenas una para la mayoría de la cadena y la otra como un área de scratch para concatenar cadenas cortas. Optimiza los anexos agrupando las operaciones cortas de anexar en una cadena pequeña y luego anexando esto a la cadena principal, reduciendo así el número de reasignaciones requeridas en la cadena principal a medida que se hace más grande.

I no han requerido este truco con std::string o std::stringstream. Creo que se usó con una biblioteca de cadenas de terceros antes de std::string, fue hace mucho tiempo. Si usted adopta una estrategia como este perfil de su aplicación primero.

 37
Author: iain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-25 17:16:19

La forma de C++ sería usar std::stringstream o simplemente concatenaciones de cadenas simples. Las cadenas de C++ son mutables, por lo que las consideraciones de rendimiento de la concatenación son menos preocupantes.

Con respecto al formato, puede hacer todo el mismo formato en una secuencia, pero de una manera diferente, similar a cout. o puede usar un funtor fuertemente escrito que encapsula esto y proporciona una cadena.Formato como interfaz e. g. boost:: format

 121
Author: jk.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-06 12:40:18

La cadena std::.la función append no es una buena opción porque no acepta muchas formas de datos. Una alternativa más útil es usar std: stringstream, así:

#include <sstream>
// ...

std::stringstream ss;

//put arbitrary formatted data into the stream
ss << 4.5 << ", " << 4 << " whatever";

//convert the stream buffer into a string
std::string str = ss.str();
 75
Author: Stu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 16:52:58

std::string es el equivalente en C++: Es mutable.

 34
Author: dan04,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-09 13:30:43

Puede usar .append () para simplemente concatenar cadenas.

std::string s = "string1";
s.append("string2");

Creo que incluso podría ser capaz de hacer:

std::string s = "string1";
s += "string2";

En cuanto a las operaciones de formateo de C#'s StringBuilder, creo que snprintf (o sprintf si desea arriesgarse a escribir código con errores ;-) ) en una matriz de caracteres y convertir de nuevo a una cadena es aproximadamente la única opción.

 9
Author: Andy Shellam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-17 14:23:14

Dado que std::string en C++ es mutable, puede usarlo. Tiene una función += operator y una append.

Si necesita añadir datos numéricos utilice las funciones std::to_string.

Si desea aún más flexibilidad en la forma de poder serializar cualquier objeto a una cadena, use la clase std::stringstream. Pero necesitarás implementar tus propias funciones de operador de streaming para que funcione con tus propias clases personalizadas.

 5
Author: Daemin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-29 02:08:13

Std:: string's += no funciona con const char* (lo que parecen ser cosas como "string to add"), así que definitivamente usar stringstream es lo más cercano a lo que se requiere - solo usa

 2
Author: sergeys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-27 12:19:06

El contenedor Rope puede valer la pena si tiene que insertar/eliminar cadena en el lugar aleatorio de la cadena de destino o para una larga secuencia de caracteres. Este es un ejemplo de la implementación de SGI:

crope r(1000000, 'x');          // crope is rope<char>. wrope is rope<wchar_t>
                                // Builds a rope containing a million 'x's.
                                // Takes much less than a MB, since the
                                // different pieces are shared.
crope r2 = r + "abc" + r;       // concatenation; takes on the order of 100s
                                // of machine instructions; fast
crope r3 = r2.substr(1000000, 3);       // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
                                // correct, but slow; may take a
                                // minute or more.
 1
Author: Igor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-24 15:42:15

Quería añadir algo nuevo por lo siguiente:

En un primer intento fallé en vencer

std::ostringstream 's operator<<

Eficiencia, pero con más intentos pude hacer un StringBuilder que es más rápido en algunos casos.

Cada vez que añado una cadena, simplemente almaceno una referencia a ella en algún lugar y aumento el contador del tamaño total.

La forma real en que finalmente lo implementé (Horror!) es utilizar un búfer opaco (std:: vector ):

  • encabezado de 1 byte (2 bits para indicar si los siguientes datos son :cadena movida, cadena o byte [])
  • 6 bits para decir la longitud del byte []

para byte [ ]

  • Almaceno directamente bytes de cadenas cortas (para acceso a memoria secuencial)

para cadenas movidas (cadenas anexas con std::move)

  • El puntero a un objeto std::string (tenemos propiedad)
  • establecer una bandera en la clase si hay reservados no utilizados bytes there

para cadenas

  • El puntero a un objeto std::string (sin propiedad)

También hay una pequeña optimización, si la última cadena insertada se movió, comprueba los bytes reservados pero no utilizados y almacena más bytes allí en lugar de usar el búfer opaco (esto es para ahorrar algo de memoria, en realidad lo hace un poco más lento, tal vez dependa también de la CPU, y es raro ver cadenas con espacio reservado adicional de todos modos)

Esto finalmente fue un poco más rápido que std::ostringstream pero tiene pocas desventajas:

  • Asumí tipos de caracteres de longitud fija (así que 1,2 o 4 bytes, no es bueno para UTF8), no estoy diciendo que no funcionará para UTF8, Solo que no lo comprobé por pereza.
  • Utilicé mala práctica de codificación (búfer opaco, fácil de hacer que no sea portátil, creo que el mío es portátil por cierto)
  • Carece de todas las características de ostringstream
  • Si se elimina alguna cadena referenciada antes de strings: comportamiento indefinido.

Conclusión? utilizar std::ostringstream

Ya soluciona el mayor cuello de botella mientras que ganar pocos puntos % de velocidad con la implementación de la mina no vale la pena las desventajas.

 0
Author: GameDeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-30 12:55:05

Un constructor de cadenas conveniente para c++

Como muchas personas respondieron antes, std::stringstream es el método de elección. Funciona bien y tiene muchas opciones de conversión y formato. IMO tiene un defecto bastante inconveniente sin embargo: No se puede utilizar como un trazador de líneas o como una expresión. Siempre tienes que escribir:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

Lo cual es bastante molesto, especialmente cuando desea inicializar cadenas en el constructor.

La razón es que a) std:: stringstream no tiene un operador de conversión a std::string y b) los operadores

La solución es anular std:: stringstream y darle mejores operadores coincidentes:

namespace NsStringBuilder {
template<typename T> class basic_stringstream : public std::basic_stringstream<T>
{
public:
    basic_stringstream() {}

    operator const std::basic_string<T> () const                                { return std::basic_stringstream<T>::str();                     }
    basic_stringstream<T>& operator<<   (bool _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (char _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (signed char _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned char _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (short _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned short _val)                   { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (int _val)                              { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned int _val)                     { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long long _val)                        { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long long _val)               { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (float _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (double _val)                           { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long double _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (void* _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::streambuf* _val)                  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ostream& (*_val)(std::ostream&))  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios& (*_val)(std::ios&))          { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (const T* _val)                         { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); }
    basic_stringstream<T>& operator<<   (const std::basic_string<T>& _val)      { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); }
};

typedef basic_stringstream<char>        stringstream;
typedef basic_stringstream<wchar_t>     wstringstream;
}

Con esto, puedes escribir cosas como

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

Incluso en el constructor.

Tengo que confesar que no medí el rendimiento, ya que tengo no se utiliza en un entorno que hace un uso intensivo de la construcción de cadenas todavía, pero supongo que no será mucho peor que std:: stringstream, ya que todo se hace a través de referencias (excepto la conversión a cadena, pero eso es una operación de copia en std::stringstream también)

 0
Author: user2328447,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-02 01:32:00