2008-09-25 18 views
30

Para mejorar la lectura de rendimiento de un archivo, intento leer todo el contenido de un gran archivo (varios MB) en la memoria y luego utilizar un istringstream para acceder a la información.¿Cómo leer el contenido del archivo en istringstream?

Mi pregunta es, ¿cuál es la mejor manera de leer esta información e "importarla" en el flujo de cadenas? Un problema con este enfoque (ver a continuación) es que al crear la secuencia de cadenas, los búferes se copian y el uso de la memoria se duplica.

#include <fstream> 
#include <sstream> 

using namespace std; 

int main() { 
    ifstream is; 
    is.open (sFilename.c_str(), ios::binary); 

    // get length of file: 
    is.seekg (0, std::ios::end); 
    long length = is.tellg(); 
    is.seekg (0, std::ios::beg); 

    // allocate memory: 
    char *buffer = new char [length]; 

    // read data as a block: 
    is.read (buffer,length); 

    // create string stream of memory contents 
    // NOTE: this ends up copying the buffer!!! 
    istringstream iss(string(buffer)); 

    // delete temporary buffer 
    delete [] buffer; 

    // close filestream 
    is.close(); 

    /* ================================== 
    * Use iss to access data 
    */ 

} 
+2

Te gusta copiar los datos. 1) Copiar en el búfer. 2) Copiar en std :: string anónimo. 3) Copia en iss. –

+0

Quizás debería buscar en los archivos mapeados en memoria. –

Respuesta

32

std::ifstream tiene un método rdbuf(), que devuelve un puntero a una filebuf. A continuación, puede "empujar" este filebuf en su stringstream:

int main() 
{ 
    std::ifstream file("myFile"); 

    if (file) 
    { 
     std::stringstream buffer; 

     buffer << file.rdbuf(); 

     file.close(); 

     // operations on the buffer... 
    } 
} 

EDIT: Como señala Martin York en los comentarios, esto podría no ser la solución más rápida desde el stringstream 's operator<< leerá el carácter filebuf por carácter. Es posible que desee comprobar su respuesta, donde utiliza el método ifstreamread como solía hacer, y luego configurar el buffer stringstream para que apunte a la memoria previamente asignada.

+0

Hola Luc, Estoy de acuerdo con tu sugerencia ... ¡la manipulación del rdbuf es el camino a seguir! ¿Pero su solución no tiene el mismo problema? ¿No creas 2 copias del mismo buffer, al menos momentáneamente? –

+6

Porque para cuando el operador <<() ve el resultado de rdbuf() es solo un búfer de secuencia, no hay concepto de un búfer de archivo en este punto, no puede buscar su longitud y por lo tanto debe usar un bucle para leer 1 char a la vez. También el búfer interno stringstream (std :: string) debe redimensionarse como datos como insertados. –

+0

Parece que está quitando nuevos caracteres de línea, que necesito. – Michele

1

Esto me parece una optimización prematura. Cuánto trabajo se está haciendo en el procesamiento. Asumiendo un escritorio/servidor moderno, y no un sistema integrado, copiar unos pocos MB de datos durante la inicialización es bastante barato, especialmente en comparación con leer el archivo fuera del disco en primer lugar. Me quedaría con lo que tienes, mediré el sistema cuando esté completo y decidiré si las posibles mejoras de rendimiento valdrían la pena. Por supuesto, si la memoria es escasa, se trata de un bucle interno, o de un programa al que se llama con frecuencia (como una vez por segundo), que cambia el equilibrio.

0

Otra cosa a tener en cuenta es que la E/S de archivo siempre va a ser la operación más lenta. La solución de Luc Touraille es correcta, pero hay otras opciones. Leer todo el archivo en la memoria de una vez será mucho más rápido que las lecturas por separado.

40

OK. No digo que esto sea más rápido que leer desde el archivo

Pero este es un método donde crea el búfer una vez y después de que los datos se leen en el búfer, utilícelos directamente como origen para stringstream.

N.B. Vale la pena mencionar que std :: ifstream está almacenado en el búfer. Lee datos del archivo en fragmentos (relativamente grandes). Las operaciones de flujo se realizan contra el búfer y solo regresan al archivo para otra lectura cuando se necesitan más datos. Entonces, antes de aspirar todos los datos en la memoria, verifique que se trata de un cuello de botella.

#include <fstream> 
#include <sstream> 
#include <vector> 

int main() 
{ 
    std::ifstream  file("Plop"); 
    if (file) 
    { 
     /* 
     * Get the size of the file 
     */ 
     file.seekg(0,std::ios::end); 
     std::streampos   length = file.tellg(); 
     file.seekg(0,std::ios::beg); 

     /* 
     * Use a vector as the buffer. 
     * It is exception safe and will be tidied up correctly. 
     * This constructor creates a buffer of the correct length. 
     * 
     * Then read the whole file into the buffer. 
     */ 
     std::vector<char>  buffer(length); 
     file.read(&buffer[0],length); 

     /* 
     * Create your string stream. 
     * Get the stringbuffer from the stream and set the vector as it source. 
     */ 
     std::stringstream  localStream; 
     localStream.rdbuf()->pubsetbuf(&buffer[0],length); 

     /* 
     * Note the buffer is NOT copied, if it goes out of scope 
     * the stream will be reading from released memory. 
     */ 
    } 
} 
+3

@Martin York, ¿cómo se puede aprender estos detalles, leer o investigar cuando se encuentra con un problema y, a su vez, aprender todos estos detalles? Muchas gracias, bdw. –

+3

@Gollum: No, esto solo son detalles obtenidos de dos áreas. 1) Usar las clases de transmisión todo el tiempo. 2) Haber implementado mis propias clases de transmisión. El número (2) hace que usted lea mucho sobre cómo se supone que debe funcionar la transmisión, porque desea que funcione de la misma manera para la transmisión, ya que funciona para las transmisiones estándar (para que pueda volver a utilizar la biblioteca STL funciones para transmisiones estándar). El único bit no intat de lo anterior está modificando cómo funciona el buffer de flujo. –

+0

¿Puede sugerir un libro o algunos recursos, quiero entender la biblioteca de plantillas estándar en profundidad (no solo usarlo, sino cómo funciona realmente en el interior) –

Cuestiones relacionadas