5 votes

Perl. Utilisation de la fonction until

J'ai un fichier de données simple. Chaque ligne du fichier contient quatre éléments. Certaines lignes sont remplies sans aucune entrée vide. D'autres lignes ont une première entrée et les trois suivantes sont vides, ou plutôt "remplies" d'un espace. C'est un fichier délimité par des tabulations.

Exemple du fichier d'entrée :

    .
    .
    .
    30  13387412    34.80391242 sSN_FIRST
    30  13387412    34.80391242 sSN5_40
    30.1             
    30.2             
    30.3             
    30.4             
    31  14740248    65.60590089 s32138223_44
    31  14740248    65.60590089 s321382_LAST
    .
    .
    .

Pour réitérer, les "vides" dans mon fichier contiennent en fait un seul espace, si cela importe.

Mon objectif global est de "remplir" la deuxième et la troisième colonne (la quatrième colonne est ignorée) dans tout le fichier. Pour cela, j'ai besoin que mon script identifie des ensembles de lignes consécutives qui sont vides, plus la ligne immédiatement précédente et la ligne immédiatement suivante de l'ensemble de lignes vides. Dans l'exemple ci-dessus, il s'agirait des lignes 2 à 7. Une fois que j'aurai réussi à le faire, je pourrai utiliser les informations des lignes adjacentes, qui sont remplies, pour aider à "remplir" les entrées manquantes dans les lignes intermédiaires.

J'ai expérimenté avec la fonction until, mais je n'arrive pas à la coupler avec une boucle qui lit les données ligne par ligne. Par exemple, je peux lire les lignes et trouver les lignes vides :

open( my $FILE, "<$mapfile" );
my @file = <$FILE>;
close $FILE;

for ( my $i = 1 ; $i < scalar @file ; $i++ ) 
    {
     my @entries = split( '\t', $file[ $i ] );
     if ( $entries[ 1 ] =~ m/ / ) 
        {
         print $file[ $i ]."\n";
        }
    }

Mais j'essaie d'utiliser la fonction until, pour lire les lignes et rechercher l'ensemble de lignes consécutives que je recherche (les lignes vides plus les deux lignes adjacentes pleines). Par exemple :

until ( $file[ une ligne ] =~ m/ / && $file[ une autre ligne ] =~ m/ / )   
    {
     mon interpolation linéaire ici;
    }

Quelqu'un pourrait-il me donner un indice sur la manière de coupler une méthode pour lire le tableau et comparer les lignes afin de trouver les ensembles dont j'ai besoin à travers le fichier ?

3voto

Robert P Points 10807

Ce que vous voulez implémenter est un algorithme de mise en cache : quelque chose qui se souvient (met en cache) des valeurs précédentes et les utilise si rien de nouveau n'apparaît. Vous n'avez même pas besoin d'une expression régulière pour cela. :)

En plus de mettre en cache les anciennes valeurs, vous devez mettre en cache les lignes intermédiaires. Comme vous aviez seulement besoin des étiquettes, vous devez simplement les conserver. Ensuite, lorsque vous atteignez la prochaine ligne complète, vous pouvez faire votre interpolation et émettre les résultats.

Voici comment je le ferais. C'est un peu plus complexe que mon exemple original, mais le même principe s'applique : stockez simplement les lignes intermédiaires, puis émettez les résultats lorsque vous atteignez votre terminal.

use strict;
use warnings;
use feature 'say';

# Obtenir les conditions de début et mettre en cache ces chiffres.

sub lire_bloc
{
   my $ligne = ;
   return 1 unless defined $ligne; # nous avons fini s'il n'y a plus rien à lire

   # Traiter et stocker les données de la première ligne dans le bloc.
   chomp $ligne;
   my ($dernière_étiquette, $dernier_chiffre1, $dernier_chiffre2, $dernière_étiquette2) = split /\t/, $ligne;

   # Continuer à lire les lignes jusqu'à trouver la fin du bloc.
   my @cache_étiquette;
   my $trouvé_dernier = 0;
   my ($étiquette1, $chiffre1, $chiffre2, $étiquette2);
   while (!$trouvé_dernier)
   {
      $ligne = ;
      chomp $ligne;
      ($étiquette1, $chiffre1, $chiffre2, $étiquette2) = split /\t/, $ligne;
      if (defined $chiffre1 && defined $chiffre2)
      {
         $trouvé_dernier = 1; # Nous avons les chiffres finaux ! Nous pouvons interpoler maintenant.
      }
      else
      {
         push @cache_étiquette, $étiquette1; 
      }
   }

   # Commencer l'affichage. Afficher la première ligne du bloc.
   say "$dernière_étiquette\t$dernier_chiffre1\t$dernier_chiffre2\t$dernière_étiquette2";

   # Calculer la pente pour l'interpolation : (dernier - premier) / différence
   my $pente1 = ($chiffre1 - $dernier_chiffre1) / (@cache_étiquette + 1);
   my $pente2 = ($chiffre2 - $dernier_chiffre2) / (@cache_étiquette + 1);
   my $distance = 0;

   # Afficher chaque étiquette et les lignes à l'intérieur.
   foreach my $étiquette (@cache_étiquette)
   {
      ++$distance;
      say $étiquette, "\t",
          $pente1 * $distance + $dernier_chiffre1, "\t",
          $pente2 * $distance + $dernier_chiffre2;
   }

   # Afficher la dernière ligne dans le bloc.
   say "$étiquette1\t$chiffre1\t$chiffre2\t$étiquette2";

   # Ce n'est pas encore fini, donc retourner une valeur 'fausse'.
   return 0;
}

# Partie principale du script

my $fini = 0;
while (! $fini)
{
   $fini = lire_bloc();
}

__DATA__
a   3   4   fin
e
f
g
h
i
k   15  26  début
k   15  26  fin
o
p
q
r
s   3   5   début
s   3   5   fin
v
w
x
y   14  16  début

émet :

a       3       4       fin
e       5       7.66666666666667
f       7       11.3333333333333
g       9       15
h       11      18.6666666666667
i       13      22.3333333333333
k       15      26      début
k       15      26      fin
o       12.6    21.8
p       10.2    17.6
q       7.8     13.4
r       5.4     9.2
s       3       5       début
s       3       5       fin
v       5.75    7.75
w       8.5     10.5
x       11.25   13.25
y       14      16      début

Ensuite, bien sûr, vous pourriez faire tout type d'arrondi ou de formatage de nombres dont vous avez besoin. :)

2voto

Kenosis Points 6136

Peut-être que ce qui suit sera utile :

use strict;
use warnings;

my ( $last, $oneColumn );

my @file = ;

for my $line (@file) {
    my @entires = split ' ', $line;

    if ( @entires == 4 ) {
        if ($oneColumn) {
            print $line;    # Ligne suivante
            $oneColumn = 0;
        }
        $last = $line;
        next;
    }

    print $last if $last;    # Ligne précédente
    undef $last;
    print $line;             # Ligne à une colonne
    $oneColumn = 1;

}

__DATA__
30  13387412    34.80391242 sSN_FIRST
30  13387412    34.80391242 sSN5_40
30.1             
30.2             
30.3             
30.4             
31  14740248    65.60590089 s32138223_44
31  14740248    65.60590089 s321382_LAST

Sortie :

30  13387412    34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31  14740248    65.60590089 s32138223_44

Une ligne 'complète' devrait avoir quatre éléments dans @entries, c'est ce que recherche if ( @entires == 4 ). S'il le trouve, il l'imprimera comme la ligne suivante seulement si des lignes à une colonne ont été imprimées. Ensuite, il sauvegarde la ligne. Les lignes sont imprimées en dehors du if uniquement lorsque la ligne n'a pas trois tabulations.

Le script suivant, plus court, produit la même sortie :

use strict;
use warnings;

my @file = ;

for ( my $i = 1 ; $i < $#file ; $i++ ) {

    if ( $file[$i] =~ /(?:\t\s){3}/ ) {
        print $file[ $i - 1 ];    # Ligne précédente

        while ( $file[$i] =~ /(?:\t\s){3}/ and $i < $#file ) {
            print $file[ $i++ ]    # Ligne à une colonne
        }

        print $file[$i];           # Ligne suivante
    }
}

__DATA__
30  13387412    34.80391242 sSN_FIRST
30  13387412    34.80391242 sSN5_40
30.1             
30.2             
30.3             
30.4             
31  14740248    65.60590089 s32138223_44
31  14740248    65.60590089 s321382_LAST

Le /(?:\t\s){3}/ correspond à trois ensembles consécutifs de tabulation et d'espace, qui ne se trouvent que sur une ligne avec une seule colonne. Lorsqu'il trouve ce pattern, il imprime la ligne précédente, puis entre dans une boucle while qui imprime les lignes à une colonne jusqu'à ce qu'une ligne complète soit trouvée ou qu'il se situe à la fin du tableau. Enfin, la ligne suivante est imprimée.

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