7
votes

Indexer énorme fichier texte

J'ai un énorme fichier texte (plus de 100 gigs) avec 6 colonnes de données (onglet comme séparateur). Dans la première colonne, j'ai une valeur entière (2500 valeurs distinctes dans l'ensemble). J'ai besoin de diviser ce fichier en plusieurs fichiers plus petits en fonction de la valeur dans la première colonne (notez que les lignes ne sont pas triées). Chacun de ces fichiers plus petits sera utilisé pour préparer un terrain à Matlab.

Je n'ai que 8 Go de RAM.

Le problème est de savoir comment faire cela efficacement? Des idées?


1 commentaires

"Seulement 8 Go de RAM". Wow, nous avons parcouru un long chemin.


6 Réponses :


5
votes

Utilisation de bash: xxx

qui scindera votre fichier 100 gig en (comme vous le dites) 2500 fichiers individuels nommés en fonction de la valeur du premier champ. Vous devrez peut-être régler l'argument de format à l'impression à votre goût.


3 commentaires

Cela va reproduire un CUT et A PrintF et ouvrira et fermera le "$ chunkfile" pour chaque ligne. Ne semble pas trop efficace pour un fichier 100gig.


@Marcelo - Vous pouvez essayer d'exécuter cela en arrière-plan tout en concevant la solution efficace. Voulez-vous parier quel programme termine le premier?


@Bo Persson Je m'attends à pirater une solution légèrement plus efficace dans votre langue rad préférée (Perl, Python, etc.) résoudrait le problème dans le temps moins écoulé. Un fichier de 100 Go a probablement plus d'un milliard de lignes.



0
votes

dans votre shell ... xxx

qui scindera un fichier volet foo dans foo1 via foon où N est déterminé par le nombre de lignes dans l'original divisé par la valeur que vous fournissez à -l. Itérer sur les morceaux dans une boucle ...

edit ... bon point dans le commentaire ... Ce script (ci-dessous) lira la ligne par ligne, classer et assignera à un fichier basé sur le premier champ ... xxx


5 commentaires

-1 parce que cela ne répond pas à la question. Vous devez devoir décider de quel fichier une ligne est déversé dans et Split ne le fait pas.


Aha - en lisant la question pour la troisième fois, j'ai constaté que c'était incomplet :)


un des rares cas où je préfère bash sur python: une doublure avec awk


@DAVKA, Will Est-ce que ONELLINER a lu le fichier ligne-by-ligne ou sous forme d'octet-ruisseau continu?


Voulez-vous dire quel est le mode I / S sous-jacent? Je ne sais pas. Si vous voulez dire du point de vue de l'utilisateur, alors par ligne



0
votes

La manière la plus efficace sera bloquée par bloc, ouvrant tous les fichiers à la fois et réutiliser le tampon de lecture pour écrire. Comme l'information est fournie, il n'existe aucun autre modèle dans les données qui pourraient être utilisées pour accélérer.

Vous ouvrirez chaque fichier dans un descripteur de fichier différent pour éviter d'ouvrir et de fermer avec chaque ligne. Ouvrez-les tous au début ou au paresseusement comme vous allez. Fermez-les tous avant de finir. La plupart des distributions Linux ne permettront que 1024 fichiers ouverts par défaut, vous aurez donc à la limite, dites en utilisant ulimit -n 2600 donné que vous avez la permission de le faire (voir aussi / etc / etc. / Sécurité / limites.conf ).

Allocatez un tampon, dites un couple de Ko et RAW Lire du fichier source. Itérer et garder les variables de contrôle. Chaque fois que vous atteignez une ligne de fin ou la fin de la mémoire tampon, écrivez du tampon dans le descripteur de fichier correct. Il y a quelques cas de bords que vous devrez prendre en compte, comme quand une lecture obtient une nouvelle ligne mais pas suffisante pour comprendre quel fichier devrait entrer.

Vous pouvez inverser-itérer pour éviter de traiter les premiers octets du tampon si vous découvrez la taille de la ligne minimale. Cela se révélera un peu plus délicieux mais une accélération néanmoins.

Je me demande si les E / S non bloquantes s'occupent de problèmes tels que celui-ci.


1 commentaires

Eh bien, la balise de la question est C ++ et la question concerne l'efficacité.



0
votes

La solution évidente consiste à ouvrir un nouveau fichier chaque fois que vous rencontrez une nouvelle valeur et de le garder ouvert jusqu'à la fin. Mais votre système d'exploitation pourrait ne pas vous permettre d'ouvrir 2500 fichiers à la fois. Donc, si vous ne devez le faire qu'une fois, vous pourriez le faire de cette façon:

  1. Passez dans le fichier, construisant une liste de toutes les valeurs. Trier cette liste. (Vous n'avez pas besoin de cette étape si vous savez à l'avance quelles seront les valeurs.)
  2. SET STARTINDEX à 0.
  3. Ouvrez, dites, 100 fichiers (quel que soit votre système d'exploitation avec). Celles-ci correspondent aux 100 prochaines valeurs de la liste, de la liste [startindex] à la liste [startindex + 99] . .
  4. Passez via le fichier, ottitude de ces enregistrements avec Liste [startindex] <= valeur <= Liste [startindex + 99] . .
  5. Fermer tous les fichiers.
  6. ajoutez 100 à startindex et allez à l'étape 3 si vous n'avez pas fini.

    Vous avez donc besoin de 26 passages dans le fichier.


0 commentaires

1
votes

pour Linux 64 bit (je ne suis pas sûr que cela fonctionne pour Windows), vous pouvez modifier le fichier et copier des blocs sur de nouveaux fichiers. Je pense que ce serait une façon la plus efficace de le faire.


0 commentaires

2
votes

one-liner avec bash + awk: xxx

Ceci appendra toutes les lignes de votre gros fichier à un fichier nommé comme poste de valeur de la première colonne + ".dat", par exemple. Ligne 12 aa bb cc dd ee ff ira au fichier 12.dat .


2 commentaires

Je viens de commencer à tester cette solution - semble très simple.


@gozwei: En effet. Je sais très peu d'awk, mais il y a des cas où il est indispensable