2009-11-29 19 views
12

¿Hay alguna forma de leer un número conocido de bytes, directamente en std :: string, sin crear un búfer temporal para hacerlo?Lectura directamente de std :: istream en std :: string

por ejemplo, actualmente puedo hacerlo por

boost::uint16_t len; 
is.read((char*)&len, 2); 
char *tmpStr = new char[len]; 
is.read(tmpStr, len); 
std::string str(tmpStr, len); 
delete[] tmpStr; 
+0

¿Ha pensado en utilizar un vector '' en lugar de 'string'? Si sus datos son más "brutos" que "similares a cadenas", es posible que funcionen mejor para usted y haya menos confusión sobre el acceso directo. (Se requiere que los vectores se almacenen contiguamente, por lo tanto, use '& v [0]'.) –

+0

En su mayor parte, son datos de cadena, solo incrustados en archivos binarios grandes.También solo quiero cambiar las rutinas de carga, no las 1000 líneas de código que luego usan los datos una vez cargados, lo que requeriría un cambio de std :: string. –

+0

Luego verificaría su implementación de cadena específica, y luego usaría la respuesta de GMan, asegúrese de verificar la transmisión después de 'is.read' también. –

Respuesta

11

std::string tiene una función resize podría utilizar, o un constructor que va a hacer lo mismo:

boost::uint16_t len; 
is.read((char*)&len, 2); 

std::string str(len, '\0'); 
is.read(&str[0], len); 

Esto no se ha probado, y yo no sé si las cadenas tienen el mandato de tener un almacenamiento contiguo.

+0

Las cadenas se definen como vectores. La misma contigüidad. – bmargulies

+4

No se definen como vectores, pero 21.3.4/1 implica almacenamiento contiguo. Sin embargo, hay informes de confusión y defectos sobre esa sección específica, y no estoy seguro de cuál es el consenso actual, ni de qué tan portátil sea según esa interpretación. –

+2

@Roger. No estoy de acuerdo con que 21.3.4/1 implique almacenamiento contiguo. Es la presencia de c_str() y data() lo que lo implica, pero solo porque una implementación eficiente requeriría un almacenamiento contiguo para implementarlos. Creo que la próxima versión del estándar también eliminará la ambigüedad de la situación. –

0

¿Está optimizando la longitud del código o tratando de ahorrar una copia aquí? ¿Qué pasa con el buffer temporal?

Argumentaría que en realidad estás eludiendo las protecciones de la cuerda que intenta escribir directamente, hazlo así. Si le preocupa el rendimiento de la copia en std :: string porque ha identificado que de alguna manera está afectando el rendimiento de su aplicación, trabajaría directamente con el char *.

EDIT:. Hacer más mirando ... initializing std::string from char* without copy

En la segunda respuesta, se declaró muy rotundamente que no se puede lograr lo que busca lograr (es decir, llenar un std :: string sin una iteración sobre el char * para copiar)

Eche un vistazo a su rutina de carga (¿lo puede publicar aquí quizás?) y minimice las asignaciones: nuevo y elimine ciertamente no son gratis así que al menos puede ahorrar algo de tiempo no tiene que volver a crear el búfer constantemente. Siempre me resulta útil borrarlo insertando el búfer en 0 o cancelando el primer índice de la matriz en cada iteración, pero puede eliminarlo rápidamente en interés del rendimiento una vez que tenga confianza en su algoritmo.

+0

El rendimiento de std :: string está bien, el problema es cargar los datos en ellos desde un archivo binario, que actualmente está llevando un período de tiempo inaceptablemente largo. La creación de perfiles mostró que el 70% del tiempo de carga es de lectura de cadenas, con solo el 30% de otros datos binarios o pequeños fragmentos de procesamiento, por lo que acelerar la lectura de cadenas parece ser la solución obvia para acelerar todo por un margen importante. Así que de ninguna manera quiero reemplazar std :: string en el resto del programa, lo que significaría cambiar miles de líneas, en lugar de simplemente cambiar la rutina de carga de cadenas. –

+0

¿Qué tan grande es un problema el alloc, dealloc del char * en cada iteración? ¿Qué sucede si simplemente mantienes un carácter * de tamaño suficiente (comprobando cada iteración, obviamente) y solo creas cadenas nuevas a partir de ese único carácter *? – antik

2

Se podría utilizar algo como getline:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string str; 
    getline (cin,str,' '); 
} 
+1

Esta es una buena sugerencia para otros problemas, pero no para este: entrada no formateada de un número específico de bytes. –

+0

Esto no responde a la pregunta ya que no lee un número específico de bytes. Incluso si lo hiciera, getline tiene que mirar cada byte que lee para el delimitador, que es costoso e innecesario cuando se especifica el número de bytes. Esta respuesta debe ser eliminada. – xaxxon

2

me gustaría utilizar un vector como el tampón.

boost::uint16_t len; 
is.read((char*)&len, 2); // Note if this file was saved from a different architecture 
         // then endianness of these two bytes may be reversed. 

std::vector buffer(len); // uninitialized. 
is.read(&buffer[0], len); 

std::string str(buffer.begin(),buffer.end()); 

Aunque probablemente se saldrá con la suya utilizando una cadena como el búfer (según lo descrito por GMan). No está garantizado por el estándar que los miembros de cadenas se encuentren en ubicaciones consecutivas (por lo tanto, verifique su implementación actual y haga un comentario importante que necesite verificar al transferir a otro compilador/plataforma).

+0

"No está garantizado por el estándar que los miembros de cadenas estén en ubicaciones consecutivas" <== al parecer lo ha sido desde '11 – xaxxon

+0

@xaxxon: Verdadero. Pero el código anterior no requiere cadena para almacenar elementos en ubicaciones consecutivas. Ahora, si te refieres al vector (y acabas de mencionar la cadena accidentalmente) este código sí lo hace. Pero como ha observado desde C++ 11, esto se ha garantizado. También antes de que se actualizara el estándar de C++ en 2011, se realizó una encuesta de todas las implementaciones principales (alrededor de 2007) y todas implementaron vectores como bloques contiguos (lo que facilitó la actualización del estándar). –

5

se puede utilizar una combinación de copy_n y un insert_iterator

void test_1816319() 
{ 
    static char const* fname = "test_1816319.bin"; 
    std::ofstream ofs(fname, std::ios::binary); 
    ofs.write("\x2\x0", 2); 
    ofs.write("ab", 2); 
    ofs.close(); 

    std::ifstream ifs(fname, std::ios::binary); 
    std::string s; 
    size_t n = 0; 
    ifs.read((char*)&n, 2); 
    std::istream_iterator<char> isi(ifs), isiend; 
    std::copy_n(isi, n, std::insert_iterator<std::string>(s, s.begin())); 
    ifs.close(); 
    _unlink(fname); 

    std::cout << s << std::endl; 
} 

ninguna copia, sin trucos, sin posibilidad de desbordamiento, hay un comportamiento indefinido.

+0

Si está haciendo lo que creo que está haciendo, entonces lea este [enlace] (http://www.boost.org/doc/libs/1_46_0/libs/serialization/doc/index.html) y el código eso va con eso. –

+0

No es el caso aquí, pero ¿es seguro el 'copy_n' si se encuentra el final del archivo o un error? – Liviu

+0

Creé un código usando su método: [revisión de código] (http://codereview.stackexchange.com/questions/38148/updating-a-file-through-c-streams). ¡Gracias! – Liviu

0

Una manera fácil sería:

std::istream& data 
const size_t dataSize(static_cast<size_t>(data.rdbuf()->in_avail())); 
std::string content; 
content.reserve(dataSize); 
data.read(&content[0], dataSize); 
Cuestiones relacionadas