2013-03-31 2 views
-1

Je pense avoir un problème de mise en mémoire tampon Perl car je dois lire et analyser de gros fichiers texte (créés par moi-même dans les lignes précédentes du code) pour finalement imprimer des éléments dans un autre fichier. À un certain point, après avoir lu un fichier avec 90 855 lignes et un autre du deuxième fichier, le script ne lit pas complètement une ligne du fichier.Problème de mise en mémoire tampon Perl suspecté

J'ai compté le nombre de caractères lus jusqu'à ce que cela arrive: 233 467, et j'ai donc essayé de vider le tampon et de dormir avant de lire la ligne suivante du fichier. Ça ne marche pas.

Des suggestions, s'il vous plaît?

Voici mon code:

foreach $i (@files) { 

    my $buff = 0; 

    print "Analyzing $i\n"; 
    sleep(1); 
    $program = $1 if $i =~ /(\w+)_SITES/; 

    open(FIL, $i) or die "$!: $i\n"; 
    while (<FIL>) { 

     $buff += length($_); 
     if ($buff >= 230000) { #FLUSH THE BUFFER, NOT WORKING!!! 
      $buff = 0; 
      sleep(1); 
      select((select(FIL), $| = 1)[0]); 
     } 

     undef($a); 
     unless ($. == 1) { 
      if ($o == 0) { 
       if (/^\d+\t(\S+)\t(\S+)\t(\d+)\t(\d+)\t(\S+)\t(\S+)\t(.*)/) { 
        $mirna = $1; 
        $target = $2; 
        $start = $3; 
        $end = $4; 
        $site = $5; 
        $comp_p = $6; 
        $a  = $7; 
        $j  = "${mirna}_${target}_${start}_$end"; 
        $site_nu{$j} = "$mirna\t$target\t$start\t$end\t$site\t$comp_p"; # Store each site in a hash 
       } 
       else { #DIES HERE!!! 
        die "$buff characters, in line $.:$_\n" 
       } 
      } 
      else { 
       if (/^\d+\t(\S+)\t(\S+)\t(\d+)\t(\d+)\t(\S+)\t(.*)/) { 
        $mirna  = $1; 
        $target  = $2; 
        $start  = $3; 
        $end   = $4; 
        $site  = $5; 
        $a   = $6; 
        $j   = "${mirna}_${target}_${start}_$end"; 
        $site_nu{$j} = "$mirna\t$target\t$start\t$end\t$site"; # Store each site in a hash 
       } 
      } 

Il meurt à "dies ICI !!" mourir, après avoir lu 3,413 caractères du deuxième fichier.

Cela arrive parce que la regex ne fonctionne pas puisque seulement la moitié de la ligne est dans $ _.

+0

Ce script est-il à simple thread? – Glenn

+0

La variable '$ |' contrôle le vidage automatique des * fichiers de sortie * après chaque instruction 'print'. Il n'a aucun effet sur les fichiers d'entrée, et le vidage d'un fichier d'entrée n'a aucun sens. – Borodin

+0

Si vous essayez de lire un fichier séparé par des tabulations, vous pouvez consulter ['Text :: CSV'] (http://search.cpan.org/perldoc?Text%3A%3ACSV). Vous devriez également ajouter 'use strict; utiliser des avertissements; 'à ce script et corriger les erreurs/avertissements. Puis réécrivez le code pour réduire la portée de toutes les variables à la plus petite possible, en utilisant le mot-clé 'my'. – TLP

Répondre

2

Le problème est presque certainement parce que les données ne sont pas dans votre fichier à lire.

Vous dites que le fichier est produit à partir d'une partie antérieure de votre code. Je suppose que vous avez un problème de mise en mémoire tampon à la place. Une fois que votre code a fini d'écrire le fichier, utilisez close pour vider les données restantes dans le fichier et je suppose que tout ira bien.

Vous devriez vérifier l'état du succès de votre close appel, comme celui-ci

close FILEHANDLE or die "Unable to close temporary file: $!"; 

En dehors de cela, la sagesse d'utiliser un fichier temporaire pour un si petit ampount de données au lieu de simplement garder tout en mémoire est discutable. En outre:

  • Vous devez toujours use strict et use warnings et déclarer toutes les variables à l'aide my le plus près possible de leur premier point d'utilisation. Sauf si vous avez choisi de déclarer tout en haut de votre programme (une très mauvaise idée) vous n'avez pas fait cela

  • Votre choix de noms de variables est erratique. $i pour un nom de fichier? Et $o pour - erm - quelque chose? $buff serait bien, sauf qu'il est la taille d'un Buffre fictif au lieu du tampon istelf

  • Vous devez utiliser des descripteurs de fichier lexical avec la forme à trois paramètres de open: open my $fil, '<', $i or die "$!: $i";

  • Si vous utilisez $| correctement, il est plus facile et plus lisible d'utiliser FILE->autoflush au lieu de l'astuce de permuter le handle de fichier sélectionné et le réglage $|.Pour ce faire, vous devez use IO::Handle au début de votre code, sauf si vous utilisez Perl 5 la version 14 ou version ultérieure qui charge IO::File (et donc IO::Handle) sur demande

  • Je pense qu'un split /\t/ simple serait mieux que le regex vous utilisent. Il semble aussi que vous seriez mieux avec un hachage de tableaux pour %site_nu comme celui-ci $site_nu{$j} = [$mirna, $target, $start, $end, $site, $comp_p]

  • Mettre un saut de ligne à la fin d'une arrête chaîne die perl d'afficher des informations sur les fichiers source et de données et les numéros de ligne, qui serait probablement utile pendant que vous déboguez

  • Vous feriez vous-même, et les personnes que vous demandez de l'aide, une faveur en formatant bien votre code source. Sans indentation correcte, il est très difficile de dire où commencent et se terminent les blocs de code

+0

Merci pour les commentaires, les gars. Oui, j'utilise des avertissements stricts et d'utilisation, déclare les variables avec mes et ferme avec succès tous les fichiers après l'impression. J'ai vérifié et les données sont en effet dans les fichiers à lire. Je vais essayer toutes les autres suggestions et je reviendrai à vous. Merci beaucoup. – dannyjmh

+0

Bonjour à tous. À la fin, j'ai vidé le fichier de sortie que j'utilisais, avant de commencer l'analyse des fichiers et le problème résolu. Merci à tous pour votre aide. – dannyjmh

+0

@dannyjmh: Alors vous ne pouvez pas l'avoir fermé correctement. Il est préférable de fermer le fichier que de le vider. Vous devez également déclarer vos variables dans un grand bloc au début du programme, ce qui n'est pas une bonne idée. – Borodin

Questions connexes