Faire un don ! | | style | statistiques | contactez-nous | plan | lettre d'information

Retourner aux forums || Retourner au forum Programmation.perl

Programmation.perl : parser un gros fichier

Posté par Nicolas Boulay () le 15 octobre 2004
J'ai un fichier binaire qui peut faire plusieurs Go à parser.

Je le lis avec read par bloc. Mon problème est que un motif peut être à cheval sur 2 blocs. Je penssais utiliser une sorte de fifo de taille double du buffer pour faire ça. Mais je me demande si il n'y a pas de manière plus "idiomatique".

> Lire le message (14 commentaires, moyenne: 1,6).  

Cette discussion est archivée, il n'est plus possible de laisser des commentaires.

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.

meuh

Posté par gc (page perso, ) le 15/10/2004 à 08:36. (lien). Évalué à 2.

idiomatique ?

  • [^]Re: meuh

    Posté par Nicolas Boulay () le 15/10/2004 à 08:56. (lien). Évalué à 1.

    ben une manière plus perlienne, quoi.

    Le type de code que l'on retrouverait dans un programme d'un bon codeur perl.

    J'ai un peu peur en fait que lancer une regex sur une fifo de taille double soit un peu lent :/

    • [^]Re: meuh

      Posté par gc (page perso, ) le 15/10/2004 à 10:10. (lien). Évalué à 3.

      bah. moi je ferais :

      open F, '</tmp/track01.cdda.wav' or die "open: $!\n";

      my ($buf, $prev);
      while (1) {
          my $ret = read F, $buf, 4096;
          if (!defined($ret)) {
              die "read: $!\n"
          } elsif (!$ret) {
              last;
          } else {
              if ("$prev$buf" =~ /(gc\w\w)/) {
                  print "match: $1\n";
              }
              $prev = $buf;
          }
      }

      mais y'a sûrement pire.

      • [^]Re: meuh

        Posté par Nicolas Boulay () le 15/10/2004 à 11:12. (lien). Évalué à 1.

        Je fais :

        my $bufferbin;
        my $oldbufferbin = "";
        while (read FICHIER, $bufferbin, 4*1024){
            my $fifo = $oldbufferbin.$bufferbin;    
            while ($fifo =~ /(\x1A\xCF\xFC\x1D.{2000,2100})\x1A\xCF\xFC\x1D/g){
        	my $cadu = $1;
                blablabla ....
            }
            $oldbufferbin = $bufferbin;
        }
        
        cela ressemble beaucoup à ton code. j'ai un problème par contre sur le while (/../g), j'ai un début de trame marqué par le code. Donc la fin de la trame est donné par le début de la trame suivante. Mais si j'utilise la regex d'en haut, la boucle suivante ne va pas matcher le début de la trame (qui la fin de la précédente). Il faudrait lui dire de revenir en arrière. Mais je n'ai pas encore trouvé comment.

        • [^]Re: meuh

          Posté par Nicolas Boulay () le 15/10/2004 à 11:38. (lien). Évalué à 1.

          En fait, ça marche (presque) avec :

          /(\x1A\xCF\xFC\x1D(.|\x00){2000,2100})(?=\x1A\xCF\xFC\x1D)/

          Le truc c'est (?=...) qui n'est pas pris en compte dans le déplacement.

          • [^]Re: meuh

            Posté par Nicolas Boulay () le 15/10/2004 à 13:27. (lien). Évalué à 1.

            bon en fait cela ne marche pas du tout.

            J'ai l'impression qu'il match toujours la même trame :(

            il doit y avoir un soucis avec la définition du "while (//g)"

Une solution...

Posté par Olivier Macchioni () le 15/10/2004 à 11:14. (lien). Évalué à 3.

Un truc comme ca ?


my ($buffer, $chunk);
my $SEPARATOR = 'X'; # C'est ton séparateur de motifs

while (read F, $chunk, 4096)
{
die "Read problem" if not defined $chunk;
last if not $chunk;
$buffer .= $chunk;
if ($buffer =~ s/^([^SEPARATOR]*)$SEPARATOR//)
{
ProcessOneFrame ($1);
}
}

die "Incomplete Frame ??" if $buffer;


P.S. il faut faire le même genre de choses pour les paquets IP fragmentés, ils peuvent arriver en plusieurs fois, et il faut donc "recoller les morceaux"

P.P.S code pas testé

  • [^]Re: Une solution...

    Posté par Nicolas Boulay () le 15/10/2004 à 11:20. (lien). Évalué à 1.

    " $buffer .= $chunk;" <- pas bon ça !

    à la fin buffer fait une taille énorme.

    j'ai toujours mon pb de relecture pour les 2ième passes :/

    • [^]Re: Une solution...

      Posté par Olivier Macchioni () le 15/10/2004 à 11:22. (lien). Évalué à 2.

      Oui, mais il se vide chaque fois qu'il peut :

      $buffer =~ s/^([^SEPARATOR]*)$SEPARATOR//

      (qu'on pourrait d'ailleurs avantageusement remplacer par :

      while ($buffer =~ s/^([^SEPARATOR]*)$SEPARATOR//)
      {
      ProcessOneFrame ($1);
      }

      • [^]Re: Une solution...

        Posté par Nicolas Boulay () le 15/10/2004 à 11:34. (lien). Évalué à 1.

        à oui, j'avais pas vu.

        c'est pas plutôt : "s/^([^SEPARATOR].*)$SEPARATOR//)" et c'est vrai que cela à l'avantage de bien réduire la taille buffer et d'aller plus vite j'imagine.

  • [^]Re: Une solution...

    Posté par Nicolas Boulay () le 15/10/2004 à 13:41. (lien). Évalué à 1.

    Pourrais-tu détailler la regexp que tu utilises, pourquoi une variable pourquoi 2x "^" pourquoi les '[]' etc..

    • [^]Re: Une solution...

      Posté par Olivier Macchioni () le 15/10/2004 à 14:49. (lien). Évalué à 3.

      Oulala, vaste question...

      Le premier ^ spécifie "Début de chaine"

      Les [] veulent dire "Un des caractères parmi... et le ^ dans les [] effectue une négation, donc "aucun des caractères parmi".

      De façons plus générale, il est difficile (et inutile) d'expliquer ce que sont les regexp et comment elles fonctionnent dans un forum... Plutôt te renvoyer vers quelques sources d'information :

      - perldoc perlrequick ou perldoc perlretut ou perldoc perlre ou encore perldoc perlreref (il faut avoir installé le package de documentation Perl qui va bien)
      - l'excellent livre consacré aux regexp de chez O'Reilly http://www.oreilly.com/catalog/regex/(...)
      - google
      - et le sympatique Vrex qui a tout pour plaire à un débutant : http://page.sourceforge.net/vrex.html(...)

      Bon courage ! (et comment as-tu pu vivre sans elles ?)

      • [^]Re: Une solution...

        Posté par Nicolas Boulay () le 15/10/2004 à 14:56. (lien). Évalué à 2.

        Je connais les regexp, je ne savais pas que "^" signifiait une négation, je croyais que cela voulais dire une nouvelle fois "début de fichier". Ce qui m'embrouillais sur le reste

        • [^]Re: Une solution...

          Posté par Nicolas Boulay () le 18/10/2004 à 11:55. (lien). Évalué à 1.

          Bon, j'ai eu quelques soucis en plus.

          Mon délimiteur fait plusieurs caractère de long donc le "truc" [^abc] ne marche pas. Ensuite, je parse du binaire et "." ne peut pas prendre certaine valeur notement "\n", il faut rajouter /s pour cela.

          Donc ma regexp est devenu :

          s/(\x1a\xcf\xfc\x1d.{2000,2100})((?=\x1a\xcf\xfc\x1d)|$)//s

          (?=) pour la présence du délimiteur à la fin
          /s pour ne pas se faire avoir avec '.'

          vala merci pour l'aide !

Revenir en haut de page || Retourner aux forums || Retourner au forum Programmation.perl