Sorte de très gros fichier texte en PowerShell
J'ai norme fichiers log Apache, entre 500 mo et 2 GO en taille. J'ai besoin de trier les lignes (chaque ligne commence par une date aaaa-MM-jj hh:mm:ss, donc pas de traitement nécessaire pour le tri.
La plus simple et la plus évidente qui vient à l'esprit est
Get-Content unsorted.txt | sort | get-unique > sorted.txt
Je suppose (sans avoir essayé) que le faire à l'aide de Get-Content
prendrait une éternité dans mon 1 go de fichiers. Je ne sais pas mon chemin autour de System.IO.StreamReader
, mais je suis curieux de savoir si une solution efficace pourrait être mis en place à l'aide de qui?
Merci à toute personne qui pourrait être plus efficace de l'idée.
[modifier]
J'ai essayé par la suite, et il a fallu un temps très long; quelques 10 minutes pour 400 MO.
sorted.txt
) a terminé deux fois la taille de la source.Les différences de taille est probablement en raison de différents encodages utilisés. Remplacement de la
> sorted.txt
avec quelque chose comme | Set-Content sorted.txt
pourrait faire l'affaire, sinon, vous pouvez essayer de | Out-File sorted.txt -Encoding <your choice>
.À l'aide de votre suggestion
| Set-Content sorted.txt
a aidé à faire le tri correctement, mais il est encore assez lent. L'ajout de -ReadCount 5000
après Get-Content
fait est beaucoup plus rapide, mais le tri est cassé. Je devine, afin de trier correctement, nous devons la lire ligne par ligne, plutôt que d'un bloc à la fois... je souhaite qu'il y avait un moyen plus efficace.Pour info; ce n'est pas quelque chose que j'ai testé, je suis contente d'invoquer la logique. Si je me trompe, je serai heureux d'être corrigé - je n'ai pas l'esprit de l'apprentissage... 🙂 Pour l'unicité, si la première ligne et la dernière d'un 10K en ligne de fichier en double (il semble improbable dans un fichier journal qui, très probablement, ajoute), vous pourriez être obligé de stocker tout ça dans la mémoire avant d'être en mesure de faire le tri que seules les lignes. Les ménagères de choses que vous êtes en train de vivre doit être en morceaux de 5000 lignes, comme d'avoir les 5000 premières lignes apparaissant après les lignes 5001-10000 dans un 10K en ligne de fichier. Chaque morceau de 5K lignes doivent être triés.
les entrées de journal sont sans doute unique. C'est un standard d'Apache fichier journal des accès, de l'enregistrement, l'accès au serveur web, donc pas de deux dossiers sont les mêmes (il faut au moins une fraction de seconde de différence pour les deux requêtes consécutives à partir de la même adresse IP et même avec d'autres paramètres).
OriginalL'auteur Predrag Vasić | 2015-09-03
Vous devez vous connecter pour publier un commentaire.
Get-Content
est terriblement inefficace pour la lecture des fichiers volumineux.Sort-Object
n'est pas très rapide, trop.Nous allons définir une ligne de base:
Avec 40 MO fichier de 1,6 million de lignes (100k unique des lignes répété 16 fois) ce script génère la sortie suivante sur ma machine:
Totalement anodin: plus de 6 minutes pour trier minuscule fichier. Chaque étape peut être beaucoup améliorée. Nous allons utiliser
StreamReader
de lire le fichier ligne par ligne dansHashSet
qui permettra de supprimer les doublons, puis copier les données àList
et de les trier, puis utilisezStreamWriter
pour le vidage des résultats.ce script produit:
Sur le même fichier en entrée, il s'exécute plus de 10 fois plus rapide. Je suis toujours surpris si il faut 30 secondes pour lire le fichier à partir du disque.
C'est une amélioration significative de la performance, toutefois, le fichier cible est sensiblement plus petit que celui de la source. Les entrées en double semblent supprimé, je ne veux pas que ça à faire. Tout ce que j'ai à faire est de trier les lignes par ordre alphabétique; s'il y a plusieurs lignes identiques, de les garder tous. Merci pour l'aide!
Votre exemple de code appelé
Get-Unique
qui supprime les doublons. Si vous n'en avez pas besoin, puis il suffit de lire directement àList
et de tri, pas besoin d'utiliserHashSet
ici.Peut-être lire le fichier améliore si le fichier est lu comme un tout, et non pas ligne par ligne.
Je doute fortement que. La rupture dans les lignes se produire à un certain point, que ce soit lors de la lecture à partir du disque, ou lors de la lecture de la mémoire, et j'attends de mise en mémoire tampon pour faire la différence entre ces deux négligeable.
OriginalL'auteur n0rd
Si chaque ligne du journal est préfixé avec un timestamp, et le journal des messages ne contiennent pas intégré les retours à la ligne (qui nécessitent un traitement spécial), je pense que ça va prendre moins de mémoire et de temps d'exécution pour convertir le timestamp de
[String]
à[DateTime]
avant de les trier. Ce qui suit suppose que chaque entrée de journal est au formatyyyy-MM-dd HH:mm:ss: <Message>
(à noter que laHH
spécificateur de format est utilisé pour une horloge de 24 heures):Si vous êtes à la traiter le fichier d'entrée pour l'affichage interactif, vous pouvez diriger la ci-dessus dans
Out-GridView
ouFormat-Table
pour afficher les résultats. Si vous avez besoin d'enregistrer le tri des résultats, vous pouvez le tuyaux au-dessus de la façon suivante:OriginalL'auteur BACON
(Édité pour être plus clair basé sur n0rd commentaires)
C'est peut-être un problème de mémoire. Puisque vous êtes en train de charger tout le fichier en mémoire afin de les trier (et l'ajout de la surcharge de la pipe en Sorte que l'Objet-et le tuyau dans Get-Unique), il est possible que vous frappez les limites de la mémoire de la machine, et l'obliger à la page sur le disque, ce qui va ralentir les choses beaucoup. Une chose que vous pourriez envisager est diviser les journaux avant de les trier, puis l'épissage de l'ensemble.
Ce ne sera probablement pas correspondre à votre format exactement, mais si j'ai un gros fichier journal pour, disons, 8/16/2012 qui s'étend sur plusieurs heures, je peux la découper en un fichier différent pour chaque heure en utilisant quelque chose comme ceci:
C'est la création d'une expression régulière pour chaque heure de la journée et de dumping tous les entrées de journal dans un plus petit fichier journal nommé par heure (par exemple 16.journal, 17.le journal).
Alors, je peux utiliser votre processus de tri et d'obtenir les entrées uniques sur une beaucoup plus petite sous-ensembles, qui doit courir beaucoup plus vite:
Et puis vous pouvez épissure ensemble.
En fonction de la fréquence des journaux, il pourrait faire plus de sens de les diviser par jour, ou minutes; la chose principale est de les obtenir dans de plus petits morceaux gérables pour le tri.
Encore une fois, cela n'a de sens que si vous frappez les limites de la mémoire de la machine (ou si Tri-Objet est d'utiliser un algorithme inefficace).
il dépend de la taille du fichier, la quantité de mémoire de la machine est disponible, l'algorithme de Tri-Objet, et de la façon dont près de trier les données à l'avance.
Sur les mêmes données d'entrée de tri de l'ensemble ne serait jamais plus lent que le tri des morceaux avec le même algorithme, puis les fusionner. Pour les externes de tri (lorsque toutes les données ne rentre pas dans la mémoire), oui, vous avez à split, tri et la fusion. Sinon il n'y a pas de gain à faire.
Révision: ci-dessus est vraie pour tout décent (O(nlogn) le temps de la complexité) algorithme de tri (sinon, il pourrait être accéléré par le fractionnement, le tri et fusion), mais pas vrai pour tout le pire des algorithmes. Je suis assez sûr de Tri-Objet utilise quelque chose de décent. Envoi de données par le biais de pipeline peuvent contribuer beaucoup de temps d'exécution.
Je vais mettre à jour ma réponse à être plus clair sur le problème (potentiel) c'est la fixation.
OriginalL'auteur E.Z. Hart