62 votes

Comment lire un grand fichier txt (1 Go) en .NET ?

J'ai un fichier texte de 1 Go que je dois lire ligne par ligne. Quelle est la meilleure et la plus rapide façon de le faire ?

private void ReadTxtFile()
{            
    string filePath = string.Empty;
    filePath = openFileDialog1.FileName;
    if (string.IsNullOrEmpty(filePath))
    {
        using (StreamReader sr = new StreamReader(filePath))
        {
            String line;
            while ((line = sr.ReadLine()) != null)
            {
                FormatData(line);                        
            }
        }
    }
}

Sur FormatData() Je vérifie le mot de départ de la ligne qui doit correspondre à un mot et, en fonction de cela, j'incrémente une variable entière.

void FormatData(string line)
{
    if (line.StartWith(word))
    {
        globalIntVariable++;
    }
}

0 votes

Vous pouvez afficher FormatData (ou une version simplifiée), juste au cas où.

0 votes

@Matthew : ignorez simplement FormatData(), en fait tout le processus est lent, donc pour le dépannage je l'ai commenté.

0 votes

Vous ne pouvez pas ignorer le FormatData si vous voulez une solution rapide, il est préférable de formater les données dans un thread séparé de celui qui lit les données.

52voto

Saurabh Points 11097

Si vous utilisez .NET 4.0, essayez Fichier en mémoire qui est une classe conçue pour ce scénario.

Vous pouvez utiliser StreamReader.ReadLine autrement.

45 votes

Si vous ne faites que de la lecture séquentielle, il est préférable d'utiliser StreamReader plutôt que MemoryMappedFile car il est beaucoup plus rapide. Le mappage de mémoire est meilleur pour les accès aléatoires.

3 votes

De plus, vous ne pouvez probablement pas créer un ViewAccesor couvrant la totalité des 1 gb, vous devez donc gérer cela ainsi que l'analyse des sauts de ligne. Les flux de fichiers sont 10 fois plus rapides que les fichiers mappés en mémoire pour la lecture séquentielle.

3 votes

Pour votre information, il y a une petite discussion à ce sujet dans l'excellent ouvrage d'O'Reilly "C# 4.0 in a Nutshell", page 569. Pour des E/S séquentielles et une taille de fichier de 1 Go, les MemoryMappedFiles sont définitivement excessifs et peuvent ralentir les choses.

31voto

konrad Points 2010

L'utilisation de StreamReader est probablement la meilleure solution puisque vous ne voulez pas avoir tout le fichier en mémoire en même temps. MemoryMappedFile est plus destiné à l'accès aléatoire qu'à la lecture séquentielle (il est dix fois plus rapide pour la lecture séquentielle et le mappage de la mémoire est dix fois plus rapide pour l'accès aléatoire).

Vous pouvez également essayer de créer votre lecteur de flux à partir d'un flux de données avec FileOptions défini sur SequentialScan ( http://msdn.microsoft.com/en-us/library/system.io.fileoptions.aspx ) mais je doute que cela fasse une grande différence.

Il y a cependant des moyens de rendre votre exemple plus efficace, puisque vous effectuez votre formatage dans la même boucle que la lecture, vous gaspillez des cycles d'horloge. Si vous voulez encore plus de performances, il serait préférable d'utiliser une solution asynchrone multithread où un thread lit les données et un autre les formate dès qu'elles sont disponibles. Consultez BlockingColletion qui pourrait répondre à vos besoins :

http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx

Si vous voulez obtenir les performances les plus rapides possibles, d'après mon expérience, le seul moyen est de lire séquentiellement un gros morceau de données binaires et de le désérialiser en texte en parallèle, mais le code commence à se compliquer à ce stade.

1 votes

+1 Le facteur limitant va être la vitesse de lecture à partir du disque, donc pour améliorer les performances, il faut que différents threads lisent et traitent les lignes.

0 votes

@Homde la dernière partie que vous avez écrite est en fait ce que StreamReader fait en interne, alors pourquoi s'embêter ?

16voto

dtb Points 104373

Vous pouvez utiliser LINQ :

int result = File.ReadLines(filePath).Count(line => line.StartsWith(word));

Fichier.LireLignes renvoie un IEnumerable<String> qui lit paresseusement chaque ligne du fichier sans charger le fichier entier en mémoire.

Enumerable.Count compte les lignes qui commencent par ce mot.

Si vous appelez cette fonction à partir d'un thread de l'interface utilisateur, utilisez une balise Travailleur d'arrière-plan .

10voto

astander Points 83138

Probablement pour le lire ligne par ligne.

Il est préférable de ne pas essayer de le forcer à se mémoriser en le lisant jusqu'au bout puis en le traitant.

8voto

Matthew Flaschen Points 131723

StreamReader.ReadLine devrait fonctionner correctement. Laissez le framework choisir la mise en mémoire tampon, sauf si vous savez, grâce au profilage, que vous pouvez faire mieux.

1 votes

StreamReader.ReadLine fonctionne bien pour les petits fichiers, mais lorsque je l'ai essayé pour les gros fichiers, il est très lent et ne répond pas toujours.

0 votes

@Mathew : le code posté, regardez-le, la longueur des lignes n'est pas fixe, certaines lignes ne contiennent que 200 mots et d'autres en contiennent 2000 ou plus.

0 votes

2000 n'est pas une somme énorme. Cela ne représente que 20 Ko, si l'on parle de mots anglais. Cependant, il est toujours préférable d'appeler le Constructeur FileStream manuellement, en spécifiant la taille du tampon. Je pense aussi que FormatData peut en fait être le problème. Cette méthode ne garde pas toutes les données en mémoire, n'est-ce pas ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X