web-dev-qa-db-fra.com

Comment lire le contenu d'un fichier dans istringstream?

Afin d'améliorer les performances de lecture à partir d'un fichier, j'essaie de lire tout le contenu d'un gros fichier (plusieurs Mo) en mémoire, puis d'utiliser un istringstream pour accéder aux informations.

Ma question est, quelle est la meilleure façon de lire ces informations et de les "importer" dans le flux de chaînes? Un problème avec cette approche (voir ci-dessous) est que lors de la création du flux de chaînes, les tampons sont copiés et l'utilisation de la mémoire double.

#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
   */

}
36
Marcos Bento

_std::ifstream_ possède une méthode rdbuf(), qui renvoie un pointeur vers un filebuf. Vous pouvez ensuite "pousser" ce filebuf dans votre stringstream:

_#include <fstream>
#include <sstream>

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

    if ( file )
    {
        std::stringstream buffer;

        buffer << file.rdbuf();

        file.close();

        // operations on the buffer...
    }
}
_

EDIT: Comme Martin York le fait remarquer dans les commentaires, ce n'est peut-être pas la solution la plus rapide car le _operator<<_ de stringstream lira le fichier fichier caractère par caractère. Vous voudrez peut-être vérifier sa réponse, où il utilise la méthode ifstream de read comme vous le faisiez, puis définir le tampon stringstream pour pointer vers le précédemment alloué Mémoire.

41
Luc Touraille

D'ACCORD. Je ne dis pas que ce sera plus rapide que de lire le fichier

Mais c'est une méthode où vous créez le tampon une fois et après que les données sont lues dans le tampon, utilisez-le directement comme source pour le flux de chaînes.

N.B.Il convient de mentionner que std :: ifstream est tamponné. Il lit les données du fichier en morceaux (relativement gros). Les opérations de flux sont effectuées sur le tampon et ne reviennent au fichier pour une autre lecture que lorsque davantage de données sont nécessaires. Donc, avant d'aspirer toutes les données en mémoire, veuillez vérifier qu'il s'agit d'un goulot d'étranglement.

#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.
         */
    }
}
41
Martin York

Cela me semble une optimisation prématurée. Combien de travail est effectué dans le traitement. En supposant un ordinateur de bureau/serveur moderne et non un système intégré, la copie de quelques Mo de données pendant l'initialisation est assez bon marché, en particulier par rapport à la lecture du fichier sur le disque en premier lieu. Je m'en tenir à ce que vous avez, mesurer le système lorsqu'il est terminé et décider si les gains de performances potentiels en valent la peine. Bien sûr, si la mémoire est serrée, c'est dans une boucle interne, ou un programme qui est souvent appelé (comme une fois par seconde), qui change l'équilibre.

1
KeithB

Une autre chose à garder à l'esprit est que les E/S de fichiers seront toujours l'opération la plus lente. La solution de Luc Touraille est correcte, mais il existe d'autres options. La lecture du fichier entier dans la mémoire à la fois sera beaucoup plus rapide que les lectures séparées.

0
luke