cómo preasignar la memoria para un objeto std :: string?

Necesito copiar un archivo en una cadena. Necesito de alguna manera preasignar memoria para ese objeto de cadena y una forma de leer directamente el contenido del archivo en la memoria de esa cadena?

std::string tiene un método .reserve para preasignación.

 std::string s; s.reserve(1048576); // reserve 1 MB read_file_into(s); 

Esto no es tanto una respuesta en sí misma, sino una especie de comentario sobre / resumen / comparación de un par de otras respuestas (así como una demostración rápida de por qué he recomendado el estilo del código @Johannes – litb da en su respuesta). Dado que @sbi publicó una alternativa que se veía bastante bien, y (especialmente) evitó la copia adicional involucrada en la lectura de un stringstream, luego .str() miembro .str() para obtener una cadena, decidí escribir una comparación rápida de los dos. :

[Editar: He agregado un tercer caso de prueba usando el código basado en istreambuf_iterator @Tyler McHenry, y agregué una línea para imprimir la longitud de cada cadena que se leyó para asegurarme de que el optimizador no optimizó la lectura porque el resultado nunca fue usado.]

[Editar2: Y ahora, el código de Martin York se ha agregado también …]

 #include  #include  #include  #include  #include  #include  int main() { std::ostringstream os; std::ifstream file("equivs2.txt"); clock_t start1 = clock(); os << file.rdbuf(); std::string s = os.str(); clock_t stop1 = clock(); std::cout << "\ns.length() = " << s.length(); std::string s2; clock_t start2 = clock(); file.seekg( 0, std::ios_base::end ); const std::streampos pos = file.tellg(); file.seekg(0, std::ios_base::beg); if( pos!=std::streampos(-1) ) s2.reserve(static_cast(pos)); s2.assign(std::istream_iterator(file), std::istream_iterator()); clock_t stop2 = clock(); std::cout << "\ns2.length = " << s2.length(); file.clear(); std::string s3; clock_t start3 = clock(); file.seekg(0, std::ios::end); s3.reserve(file.tellg()); file.seekg(0, std::ios::beg); s3.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); clock_t stop3 = clock(); std::cout << "\ns3.length = " << s3.length(); // New Test std::string s4; clock_t start4 = clock(); file.seekg(0, std::ios::end); s4.resize(file.tellg()); file.seekg(0, std::ios::beg); file.read(&s4[0], s4.length()); clock_t stop4 = clock(); std::cout << "\ns4.length = " << s3.length(); std::cout << "\nTime using rdbuf: " << stop1 - start1; std::cout << "\nTime using istream_iterator: " << stop2- start2; std::cout << "\nTime using istreambuf_iterator: " << stop3 - start3; std::cout << "\nTime using read: " << stop4 - start4; return 0; } 

Ahora la parte impresionante: los resultados. Primero con VC ++ (en caso de que a alguien le importe, el código de Martin es lo suficientemente rápido, aumenté el tamaño del archivo para obtener un tiempo significativo):

s.length () = 7669436
s2.length = 6390688
s3.length = 7669436
s4.length = 7669436
Tiempo usando rdbuf: 184
Tiempo usando istream_iterator: 1332
Tiempo usando istreambuf_iterator: 249
Tiempo usando lectura: 48

Luego con gcc (cygwin):

s.length () = 8278035
s2.length = 6390689
s3.length = 8278035
s4.length = 8278035
Tiempo usando rdbuf: 62
Tiempo usando istream_iterator: 2199
Tiempo usando istreambuf_iterator: 156
Tiempo usando lectura: 16

[fin de la edición: las conclusiones permanecen, aunque el ganador ha cambiado - el código de Martin es claramente el más rápido. ]

Los resultados son bastante consistentes con respecto a cuál es el más rápido y el más lento. La única incoherencia es con cuánto más rápido o más lento es uno que otro. Aunque las ubicaciones son las mismas, las diferencias de velocidad son mucho mayores con gcc que con VC ++.

Esto debería ser todo lo que necesitas:

 ostringstream os; ifstream file("name.txt"); os << file.rdbuf(); string s = os.str(); 

Esto lee los caracteres del file y los inserta en el flujo de cadenas. Luego obtiene la cadena creada detrás de las escenas. Tenga en cuenta que caí en la siguiente trampa: Al usar el operador de extracción se saltará el espacio en blanco inicial. noskipws usar el operador de inserción como se noskipws arriba, o usar el manipulador noskipws :

 // Beware, skips initial whitespace! file >> os.rdbuf(); // This does not skip it file >> noskipws >> os.rdbuf(); 

Sin embargo, estas funciones se describen como la lectura de la secuencia carácter por carácter (aunque no estoy seguro de qué optimizaciones son posibles), no las he cronometrado para determinar su velocidad.

Solo por diversión, aquí hay otra forma de hacer esto:

 // Beware, brain-compiled code ahead! std::ifstream ifs( /* ... */ ); if( !ifs.good() ) return; // whatever std::string str; ifs.seekg( 0, std::ios_base::end ); const std::streampos pos = ifs.tellg(); ifs.seekg( 0, std::ios_base::beg ); if( pos!=std::streampos(-1) ) // can get stream size? str.reserve(static_cast(pos)); str.assign( std::istream_iterator(ifs) , std::istream_iterator() ); 

Espero no haberlo explotado demasiado.

std :: string :: reserve ()

std :: getline ()

Parece que estás preguntando cómo hacer una operación de tipo CString :: GetBuffer, ReleaseBuffer con std :: string.

No sé de ninguna manera de hacer esto directamente, una forma fácil sería simplemente crear un búfer de estilo C en bruto, leer en el búfer y luego copiar el búfer en std :: string usando assign o lo que sea. Por supuesto, tendría que preocuparse por los problemas de desbordamiento del búfer, etc. También usaría un std :: autoptr para administrar el puntero del búfer en bruto, para enrutar la desasignación en la excepción, etc. Esto es un poco más simple que usar el stringstream, etc. Puedo proporcionar un ejemplo si es necesario.

Devin Ellingson