J'ai la belle tâche de déterminer comment gérer les gros fichiers chargés dans l'éditeur de script de notre application (c'est comme VBA pour notre produit interne pour les macros rapides). La plupart des fichiers pèsent entre 300 et 400 Ko, ce qui est un chargement correct. Mais lorsqu'ils dépassent les 100 Mo, le processus est difficile (comme vous vous en doutez).
Ce qui se passe, c'est que le fichier est lu et poussé dans un RichTextBox qui est ensuite parcouru - ne vous inquiétez pas trop de cette partie.
Le développeur qui a écrit le code initial utilise simplement un StreamReader et fait
[Reader].ReadToEnd()
ce qui pourrait prendre un certain temps.
Ma tâche est de casser ce morceau de code, de le lire en morceaux dans un tampon et d'afficher une barre de progression avec une option pour l'annuler.
Quelques hypothèses:
- La plupart des fichiers auront entre 30 et 40 Mo
- Le contenu du fichier est du texte (non binaire), certains sont au format Unix, d'autres sont DOS.
- Une fois le contenu récupéré, nous déterminons quel terminateur est utilisé.
- Personne n'est concerné une fois qu'il est chargé le temps qu'il faut pour le rendu dans la richtextbox. C'est juste la charge initiale du texte.
Maintenant pour les questions:
- Puis-je simplement utiliser StreamReader, puis vérifier la propriété Length (donc ProgressMax) et émettre une lecture pour une taille de tampon définie et parcourir dans une boucle while WHILST à l' intérieur d'un travailleur en arrière-plan, afin de ne pas bloquer le thread d'interface utilisateur principal? Puis renvoyez le générateur de chaînes au thread principal une fois qu'il est terminé.
- Le contenu ira à un StringBuilder. puis-je initialiser le StringBuilder avec la taille du flux si la longueur est disponible?
Est-ce que ce sont (selon vos opinions professionnelles) de bonnes idées? J'ai eu quelques problèmes dans le passé avec la lecture du contenu de Streams, car il manquera toujours les derniers octets ou quelque chose du genre, mais je poserai une autre question si tel est le cas.
la source
Réponses:
Vous pouvez améliorer la vitesse de lecture en utilisant un BufferedStream, comme ceci:
MISE À JOUR de mars 2013
J'ai récemment écrit du code pour la lecture et le traitement (recherche de texte dans) des fichiers texte de 1 Go (beaucoup plus volumineux que les fichiers impliqués ici) et j'ai obtenu un gain de performances significatif en utilisant un modèle producteur / consommateur. La tâche de producteur a lu des lignes de texte à l'aide de
BufferedStream
et les a transférées à une tâche de consommateur distincte qui a effectué la recherche.J'ai utilisé cela comme une opportunité pour apprendre TPL Dataflow, qui est très bien adapté pour coder rapidement ce modèle.
Pourquoi BufferedStream est plus rapide
MISE À JOUR de décembre 2014: votre kilométrage peut varier
Sur la base des commentaires, FileStream doit utiliser un BufferedStream en interne. Au moment où cette réponse a été fournie pour la première fois, j'ai mesuré une amélioration significative des performances en ajoutant un BufferedStream. À l'époque, je ciblais .NET 3.x sur une plate-forme 32 bits. Aujourd'hui, en ciblant .NET 4.5 sur une plateforme 64 bits, je ne vois aucune amélioration.
en relation
Je suis tombé sur un cas où la diffusion d'un gros fichier CSV généré vers le flux de réponse à partir d'une action ASP.Net MVC était très lente. L'ajout d'un BufferedStream a amélioré les performances de 100 fois dans ce cas. Pour en savoir plus, voir Sortie sans tampon très lente
la source
Si vous lisez les statistiques de performances et de référence sur ce site Web , vous verrez que le moyen le plus rapide de lire (car la lecture, l'écriture et le traitement sont tous différents) un fichier texte est l'extrait de code suivant:
Au total, environ 9 méthodes différentes ont été évaluées au banc, mais celle-ci semble être en avance la plupart du temps, même en exécutant le lecteur tamponné comme d'autres lecteurs l'ont mentionné.
la source
StringBuilder
pour les charger en mémoire, se charge plus rapidement car il ne crée pas de nouvelle chaîne à chaque fois que vous ajoutez des caractères)Vous dites qu'on vous a demandé d'afficher une barre de progression pendant le chargement d'un gros fichier. Est-ce parce que les utilisateurs veulent vraiment voir le pourcentage exact de chargement de fichiers, ou simplement parce qu'ils veulent un retour visuel indiquant que quelque chose se passe?
Si ce dernier est vrai, la solution devient beaucoup plus simple. Faites simplement
reader.ReadToEnd()
sur un fil d'arrière-plan et affichez une barre de progression de type rectangle au lieu d'une barre appropriée.Je soulève ce point parce que d'après mon expérience, c'est souvent le cas. Lorsque vous écrivez un programme de traitement de données, les utilisateurs seront certainement intéressés par un pourcentage complet, mais pour les mises à jour d'interface utilisateur simples mais lentes, ils sont plus susceptibles de vouloir simplement savoir que l'ordinateur n'a pas planté. :-)
la source
StreamReader
boucle. Cependant, ce sera toujours plus simple car il n'est pas nécessaire de lire à l'avance pour calculer l'indicateur de progression.Pour les fichiers binaires, le moyen le plus rapide de les lire que j'ai trouvé est le suivant.
Dans mes tests, c'est des centaines de fois plus rapide.
la source
Utilisez un travailleur d'arrière-plan et ne lisez qu'un nombre limité de lignes. En savoir plus uniquement lorsque l'utilisateur fait défiler.
Et essayez de ne jamais utiliser ReadToEnd (). C'est l'une des fonctions que vous pensez "pourquoi l'ont-ils fait?"; c'est un assistant de script pour les enfants qui va bien avec les petites choses, mais comme vous le voyez, ça craint pour les gros fichiers ...
Les gars qui vous disent d'utiliser StringBuilder doivent lire le MSDN plus souvent:
Considérations relatives aux performances
Les méthodes Concat et AppendFormat concaténent toutes deux les nouvelles données avec un objet String ou StringBuilder existant. Une opération de concaténation d'objets String crée toujours un nouvel objet à partir de la chaîne existante et des nouvelles données. Un objet StringBuilder gère une mémoire tampon pour accueillir la concaténation de nouvelles données. De nouvelles données sont ajoutées à la fin du tampon si de la place est disponible; sinon, un nouveau tampon plus grand est alloué, les données du tampon d'origine sont copiées dans le nouveau tampon, puis les nouvelles données sont ajoutées au nouveau tampon. Les performances d'une opération de concaténation pour un objet String ou StringBuilder dépendent de la fréquence à laquelle une allocation de mémoire se produit.
Une opération de concaténation String alloue toujours de la mémoire, tandis qu'une opération de concaténation StringBuilder alloue uniquement de la mémoire si la mémoire tampon d'objet StringBuilder est trop petite pour accueillir les nouvelles données. Par conséquent, la classe String est préférable pour une opération de concaténation si un nombre fixe d'objets String est concaténé. Dans ce cas, les opérations de concaténation individuelles peuvent même être combinées en une seule opération par le compilateur. Un objet StringBuilder est préférable pour une opération de concaténation si un nombre arbitraire de chaînes est concaténé; par exemple, si une boucle concatène un nombre aléatoire de chaînes d'entrée utilisateur.
Cela signifie une énorme allocation de mémoire, ce qui devient une grande utilisation du système de fichiers d'échange, qui simule des sections de votre disque dur pour agir comme la mémoire RAM, mais un disque dur est très lent.
L'option StringBuilder convient parfaitement à ceux qui utilisent le système en tant que mono-utilisateur, mais lorsque deux utilisateurs ou plus lisent des fichiers volumineux en même temps, vous rencontrez un problème.
la source
Cela devrait suffire à vous aider à démarrer.
la source
Jetez un œil à l'extrait de code suivant. Vous avez mentionné
Most files will be 30-40 MB
. Cela prétend lire 180 Mo en 1,4 seconde sur un Intel Quad Core:Article original
la source
Vous feriez peut-être mieux d'utiliser la gestion des fichiers mappés en mémoire ici . / invoque pour faire le même travail.
Edit: Voir ici sur le MSDN pour savoir comment cela fonctionne, voici l' entrée de blog indiquant comment cela est fait dans le prochain .NET 4 lorsqu'il sortira en version. Le lien que j'ai donné plus tôt est un wrapper autour du pinvoke pour y parvenir. Vous pouvez mapper le fichier entier dans la mémoire et l'afficher comme une fenêtre coulissante lors du défilement du fichier.
la source
Toutes d'excellentes réponses! cependant, pour quelqu'un qui cherche une réponse, celles-ci semblent quelque peu incomplètes.
Comme une chaîne standard ne peut que de taille X, de 2 Go à 4 Go selon votre configuration, ces réponses ne répondent pas vraiment à la question de l'OP. Une méthode consiste à travailler avec une liste de chaînes:
Certains peuvent vouloir tokeniser et diviser la ligne lors du traitement. La liste de chaînes peut désormais contenir de très gros volumes de texte.
la source
Un itérateur peut être parfait pour ce type de travail:
Vous pouvez l'appeler en utilisant ce qui suit:
Au fur et à mesure du chargement du fichier, l'itérateur renvoie le numéro de progression de 0 à 100, que vous pouvez utiliser pour mettre à jour votre barre de progression. Une fois la boucle terminée, le StringBuilder contiendra le contenu du fichier texte.
De plus, comme vous voulez du texte, nous pouvons simplement utiliser BinaryReader pour lire les caractères, ce qui garantira que vos tampons s'alignent correctement lors de la lecture de caractères multi-octets ( UTF-8 , UTF-16 , etc.).
Tout cela se fait sans utiliser de tâches d'arrière-plan, de threads ou de machines à états personnalisées complexes.
la source
Mon fichier fait plus de 13 Go:
Le lien ci-dessous contient le code permettant de lire facilement un morceau de fichier:
Lire un gros fichier texte
Plus d'information
la source