web-dev-qa-db-fra.com

Comment lire un gros fichier en c ++

Si j'ai un énorme fichier (par exemple 1 To, ou toute taille qui ne rentre pas dans la RAM. Le fichier est stocké sur le disque). Il est délimité par l'espace. Et mon RAM n'est que de 8 Go. Puis-je lire ce fichier dans ifstream? Sinon, comment lire un bloc de fichier (par exemple 4 Go)?

13
ZigZagZebra

Il y a deux ou trois choses que vous pouvez faire.

Tout d'abord, il n'y a aucun problème à ouvrir un fichier qui est plus grand que la quantité de RAM que vous avez. Ce que vous ne pourrez pas faire est de copier le fichier entier en direct dans votre mémoire. Le mieux serait que vous trouviez un moyen de lire seulement quelques morceaux à la fois et de les traiter. Vous pouvez utiliser ifstream pour cet objectif (avec ifstream.read , par exemple). Allouez, disons, un mégaoctet de mémoire, lisez le premier mégaoctet de ce fichier, rincez et répétez:

ifstream bigFile("mybigfile.dat");
constexpr size_t bufferSize = 1024 * 1024;
unique_ptr<char[]> buffer(new char[bufferSize]);
while (bigFile)
{
    bigFile.read(buffer.get(), bufferSize);
    // process data in buffer
}

Une autre solution consiste à mapper le fichier en mémoire. La plupart des systèmes d'exploitation vous permettent de mapper un fichier en mémoire même s'il est plus grand que la quantité physique de mémoire dont vous disposez. Cela fonctionne parce que le système d'exploitation sait que chaque page de mémoire associée au fichier peut être mappée et non mappée à la demande: lorsque votre programme a besoin d'une page spécifique, le système d'exploitation la lira du fichier dans la mémoire de votre processus et échangera une page qui n'a pas été utilisé depuis un certain temps.

Cependant, cela ne peut fonctionner que si le fichier est plus petit que la quantité maximale de mémoire que votre processus peut théoriquement utiliser. Ce n'est pas un problème avec un fichier de 1 To dans un processus 64 bits, mais cela ne fonctionnerait pas dans un processus 32 bits.

Aussi soyez conscient des esprits que vous invoquez . Le mappage en mémoire d'un fichier n'est pas la même chose que sa lecture. Si le fichier est soudainement tronqué à partir d'un autre programme, votre programme est susceptible de se bloquer. Si vous modifiez les données, il est possible que vous manquiez de mémoire si vous ne pouvez pas sauvegarder sur le disque. En outre, l'algorithme de votre système d'exploitation pour la pagination dans et hors de la mémoire peut ne pas se comporter d'une manière qui vous avantage considérablement. En raison de ces incertitudes, j'envisagerais de mapper le fichier uniquement si sa lecture en morceaux à l'aide de la première solution ne peut pas fonctionner.

Sous Linux/OS X, vous utiliseriez mmap pour cela. Sous Windows, vous ouvrez un fichier, puis utilisez CreateFileMapping puis MapViewOfFile.

15
zneak

Je suis sûr que vous n'avez pas besoin de garder tout le fichier en mémoire. En règle générale, on veut lire et traiter le fichier par morceaux. Si vous voulez utiliser ifstream, vous pouvez faire quelque chose comme ça:

ifstream is("/path/to/file");
char buf[4096];
do {
    is.read(buf, sizeof(buf));
    process_chunk(buf, is.gcount());
} while(is);
4
Oleg Andriyanov

Une approche plus avancée consiste à, au lieu de lire le fichier entier ou ses morceaux en mémoire, vous pouvez le mapper en mémoire à l'aide d'API spécifiques à la plate-forme:

Sous Windows: CreateFileMapping (), MapViewOfFile ()

Sous Linux: open (2)/creat (2), shm_open, mmap

vous devrez compiler l'application 64 bits pour la faire fonctionner.

pour plus de détails, voir ici: CreateFileMapping, MapViewOfFile, comment éviter de bloquer la mémoire système

2
marcinj