2008-12-12 11 views
36

Tengo una situación donde tengo que procesar grandes (muchos de GB) cantidades de datos como tal:inicializar std :: string de char * sin copia

  1. construir una cadena grande añadiendo muchas más pequeña (char C *) cadenas
  2. recortar la cadena
  3. convertir la cadena en un C++ const std :: string para su procesamiento (sólo lectura)
  4. repetición

el da ta en cada iteración son independientes.

Mi pregunta es, me gustaría minimizar (si es posible eliminar) el uso de la memoria asignada en el montón, ya que en este momento es mi mayor problema de rendimiento.

¿Hay alguna forma de convertir una cadena C (char *) en una cadena stl C++ (std :: string) sin que se requiera que std :: string aloje/copie los datos internamente?

Como alternativa, ¿podría utilizar cadenas de caracteres o algo similar para volver a utilizar un gran buffer?

Editar: Gracias por las respuestas, para mayor claridad, creo que una pregunta revisada sería:

¿Cómo se puede construir I (a través de múltiples APPENDs) una secuencia de C++ stl eficiente. Y si realiza esta acción en un bucle, donde cada bucle es totalmente independiente, ¿cómo puedo volver a utilizar este espacio asignado?

Respuesta

17

¿Es posible utilizar una cadena de C++ en el paso 1? Si usa string::reserve(size_t), puede asignar un búfer lo suficientemente grande para evitar múltiples asignaciones de montón al agregar las cadenas más pequeñas, y luego puede usar la misma cadena de C++ en todos los pasos restantes.

Consulte this link para obtener más información sobre la función reserve.

+0

Tu solución suena como lo que yo busco, aunque, cuando dices "puedes usar esa misma cadena de C++" ¿te refieres a usar clear() y luego continuar construyendo la siguiente cadena? – Akusete

+0

Esta solución es la más simple de adoptar, actualmente, aunque no estoy seguro si la reutilización está realmente especificada por el estándar (aunque parece que funciona en mi implementación) – Akusete

+0

Usar clear() debería funcionar.Hasta donde yo sé, clear() no libera la memoria utilizada por la cadena, y por lo tanto no afecta el espacio asignado por reserva(). –

1

¿Es cada iteración lo suficientemente independiente como para poder usar la misma cadena std :: para cada iteración? Uno esperaría que su implementación de std :: string sea lo suficientemente inteligente como para reutilizar la memoria si le asigna una const char * cuando se usaba anteriormente para otra cosa.

Asignar un char * en una std :: string siempre debe copiar al menos los datos. La administración de la memoria es una de las razones principales para usar std :: string, por lo que no podrá anularla.

7

Para ayudar con cadenas realmente grandes SGI tiene la clase Cuerda en su STL.
No estándar, pero puede ser útil.

http://www.sgi.com/tech/stl/Rope.html

Al parecer, la cuerda se encuentra en la próxima versión de la norma :-)
Nota broma del desarrollador. Una cuerda es una gran cadena. (Ha Ha) :-)

18

No se puede formar una std :: cadena sin copiar los datos. Un stringstream probablemente reutilizaría la memoria de pasada a pasada (aunque creo que el estándar no dice si realmente tiene que hacerlo), pero aún así no evitaría la copia.

Un enfoque común para este tipo de problema es escribir el código que procesa los datos en el paso 3 para usar un par de iteradores de inicio/final; luego puede procesar fácilmente una std :: string, un vector de caracteres, un par de punteros crudos, etc. A diferencia de pasarle un tipo de contenedor como std :: string, ya no sabría ni importaría cómo se asignó la memoria, ya que todavía pertenecería a la persona que llama. Llevando esta idea a su conclusión lógica es boost::range, que agrega todos los constructores sobrecargados para permitir que la persona que llama simplemente pase una cadena/vector/lista/cualquier tipo de contenedor con .begin() y .end(), o iteradores separados.

Después de haber escrito su código de procesamiento para trabajar en un rango de iterador arbitrario, incluso podría escribir un iterador personalizado (no tan difícil como suena, básicamente un objeto con algunos typedefs estándar, y operador ++/*/=/== /! = sobrecargado para obtener un iterador de solo avance) que se encarga de avanzar al siguiente fragmento cada vez que toca el final del que está trabajando, omitiendo el espacio en blanco (supongo que eso es lo que quiso decir con recorte) . Que nunca tuviste que ensamblar toda la cadena contiguamente. Que esto sea o no un triunfo depende de cuántos fragmentos/qué tan grande de fragmentos tenga. Esto es esencialmente lo que la cuerda SGI mencionada por Martin York es: una cadena donde el apéndice forma una lista vinculada de fragmentos en lugar de un buffer contiguo, que por lo tanto es adecuado para valores mucho más largos.


ACTUALIZACIÓN (ya que todavía veo upvotes ocasionales en esta respuesta):

C++ 17 presenta otra opción: std::string_view, que sustituyó std :: string en muchas firmas de función, no es una -propósito de referencia a los datos de un personaje. Es implícitamente convertible desde std :: string, pero también puede construirse explícitamente a partir de datos contiguos pertenecientes a otra parte, evitando la copia innecesaria std :: string impone.

+0

Creo que su solución es el mejor enfoque (cambiando el código de procesamiento) desafortunadamente en esta situación no es una opción. – Akusete

+1

¿Existe alguna forma estándar especificada para lograr la reutilización del buffer? Simplemente no quiero confiar en la implementación en una plataforma específica. – Akusete

+0

A menos que el código de procesamiento sea una función de biblioteca que no utilice iteradores ni cadenas, solo un antiguo 'char *' + tamaño. – SasQ

0

En este caso, podría ser mejor procesar el char * directamente, en lugar de asignarlo a std :: string.

+3

Sí, lo haría, aunque las entradas (C char * 's) y la salida (std :: string) no están bajo mi control. – Akusete

4

Esta es una respuesta de pensamiento lateral, que no aborda directamente la cuestión, sino que "piensa" a su alrededor. Podría ser útil, podría no ...

El procesamiento de solo lectura de std :: string realmente no requiere un subconjunto muy complejo de las características de std :: string. ¿Existe la posibilidad de que pueda buscar/reemplazar en el código que realiza todo el procesamiento en std :: cadenas, por lo que se necesita algún otro tipo en su lugar? Comience con una clase en blanco:

clase lightweight_string {};

Luego reemplace todas las referencias de std :: string con lightweight_string. Realice una compilación para averiguar exactamente qué operaciones se necesitan en lightweight_string para que actúe como un reemplazo directo. Luego puede hacer que su implementación funcione como lo desee.

Cuestiones relacionadas