2010-07-21 13 views
18

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?cómo preasignar memoria para un objeto std :: string?

+4

posible duplicado de [Leer archivo ASCII entero en C++ std :: string] (http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) - Mi respuesta aceptada a esta pregunta también explica cómo preasignar toda la memoria para que la cadena no se expanda repetidamente durante la lectura. –

Respuesta

6

Esto debería ser todo lo que necesita:

ostringstream os; 
ifstream file("name.txt"); 
os << file.rdbuf(); 

string s = os.str(); 

Esto lee caracteres de file y los inserta en la stringstream. 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. Usted tiene que utilizar el operador de inserción como la de arriba, o utilice el noskipws manipulador:

// Beware, skips initial whitespace! 
file >> os.rdbuf(); 

// This does not skip it 
file >> noskipws >> os.rdbuf(); 

Estas funciones se describen como leer el carácter corriente por el personaje, aunque (no está seguro de lo que optimizaciones son posibles aquí, sin embargo), no he t cronometró estos para determinar su velocidad.

+1

+1 golpeándome. :( – GManNickG

+1

esto copia dos veces, una vez al buffer 'ostringstream' y la segunda vez a' s' –

+0

@Johannes, asumí que la memoria de cadena era buffer contagiosa, pero después de leer la respuesta de @GMan me di cuenta de que no hay a la vuelta de la copia. –

5

Sólo por diversión, aquí es otra manera 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<std::string::size_type>(pos)); 

str.assign(std::istream_iterator<char>(ifs) 
      , std::istream_iterator<char>()); 

espero que no lo sopla demasiado mal.

+2

+1, estaba esperando a que alguien elaborara el código basado en iterador de flujo :) – bobah

+0

+1, para 'Compilador de cerebro' flexible está bien si falta'} ';) –

+0

@Gollum: Admito libremente que copié esas líneas 'seekg()' directamente de algún código mío (que llena una cadena con el contenido de un archivo) y pasé por alto el '{'. Lo arreglé, pero para eso es ese descargo de todos modos. – sbi

14

Esto no es tanto una respuesta en sí misma, sino una especie de comentario sobre/resumen/comparación de algunas otras respuestas (así como una rápida demostración de por qué he recomendado el estilo de código @ Johannes - litb da 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 usé el 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 @Tyler McHenry istreambuf_iterator, y agregué una línea para imprimir la longitud de cada cadena que se leyó para asegurar que el optimizador no optimizara la lectura porque Nunca se utilizó el resultado]

[Edit2:. Y ahora, el código de Martin York se ha añadido también ...]

#include <fstream> 
#include <sstream> 
#include <string> 
#include <iostream> 
#include <iterator> 
#include <time.h> 

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<std::string::size_type>(pos)); 
    s2.assign(std::istream_iterator<char>(file), std::istream_iterator<char>()); 
    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<char>(file)), 
      std::istreambuf_iterator<char>()); 
    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 alguien se preocupa, código de Martin es lo suficientemente rápido como he aumentado el tamaño del archivo para obtener un tiempo significativo para ella):

s.length() = 7.669.436
s2.length = 6390688
s3.length = 7669436
s4.longitud = 7669436
Tiempo usando rdbuf: 184
Tiempo usando istream_iterator: 1332
Tiempo usando istreambuf_iterator: 249
Tiempo usando leer: 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 el siguiente: 16

[fin de editar - las conclusiones siguen siendo, 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 cómo mucho más rápido o más lento que uno. Aunque las ubicaciones son las mismas, las diferencias de velocidad son mucho más grandes con gcc que con VC++.

+0

Tipo de lo que pensé inicialmente: es mucho más fácil optimizar la lectura de char por by de op << en una lectura de bloque (o partes apropiadas en línea) que la lectura de char-by-char de istream_iterator (aunque tal código tiene que use 'istreambuf_iterator' para evitar omitir el espacio en blanco para cada carácter leído, ¿tal vez eso acelerará las cosas ya que está sucediendo en un nivel inferior?), que pasa por varios pasos con op ++, op * etc. Pero no esperaba que hiciera * eso * mucha diferencia. Gracias por medir el tiempo! –

+1

¿Podría escribir qué indicadores de compilación se usaron para los casos de prueba? – tomekpe

1

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

No conozco ninguna forma 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

+1

'auto_ptr' no maneja los tipos de matriz correctamente. Solo usa 'std :: vector'. (Además, firmar publicaciones no está bien visto, su nombre está debajo de todo lo que hace.) – GManNickG

+0

Gracias GMan, me olvidé de ese problema, auto_ptr siempre llama a eliminar en lugar de eliminar [], que es lo que se necesitaría en este caso. Podrías simplemente crear una clase simple array_auto_ptr o como dices usar std :: vector. – DevinEllingson

Cuestiones relacionadas