Forum Programmation.perl besoin de commande perl pour éviter une boucle sur grep, un if et un sed (en clair un rechercher remplacer conditionnel)

Posté par  .
Étiquettes : aucune
0
8
mar.
2006
Bonjour,
J'ai un soucis de performance donc je cherche des solutions type awk voir perl.
Pour toutes les lignes commencant par ACT, je veux remplacer la valeur du champ 12 avec celle du champ 14 si la valeur du champ 12 commence par 0.
Pour l'instant j'ai une boucle qui prend toutes les lignes commencant par ACT et je teste si le champ 12 commence par zéro, si oui je fais un sed. Pour plus de performance, j'ai voulu contstruire un fichier avec les chaine sed de remplacement (s/ligne1/ligne2/g) pour exécuter un sed -f avec ce fichier en critère. Mais le sed avec fichier en critère ne fonctionne pas avec un gros fichier en critere.
La solution si elle existe serait de ne plus passer par un boucle.

exemple de ligne (séparateur = ~)
ENT~xxxxxx~20060101,000000~20060301,000000
ACT~xxxxxxxx~~xxxxxxxxxxx~xxxxxx~xxxxxxx~xxxxxx~x~xxxxxxxx~xxxxxx~x~0051~01~6020~01~~~0~~~294522~269824~0~~20060101,001500
ENA~xxxxxxxx~xxxxxx~20060101,001500~~0~20060101,004737~xxxxxxxxxxx~7~

ici sur la ligne ACT le champ 12 est 0051 il doit etre remplacé par 6020 car il commence par 0.
Merci d'avance pour votre aide. hugo.
  • # en awk:

    Posté par  . Évalué à 2.

    Pour toutes les lignes commencant par ACT, je veux remplacer la valeur du champ 12 avec celle du champ 14 si la valeur du champ 12 commence par 0.

    awk -F\~ ' /^ACT/ {
    print $0
    if (match($12,/^0/)) {
    $12=$14
    print $12 " " $14
    }
    print $0
    }' toto

    en supposant que ton fichier s'appelle toto.
    • [^] # Re: en awk:

      Posté par  . Évalué à 2.

      oups, j'ai omis de commenter:

      awk -F\~ ' /^ACT/ { # Recherche des lignes commencant par ACT
      print $0 # Affiche la ligne avant transformation
      if (match($12,/^0/)) { # si le 12e champ commence par 0
      $12=$14 # remplacer le champ 12 par le champ 14
      }



      print $0 On affiche la ligne resultante. Attention j'ai omis un truc:si on veut conserver les ~ en separateur de champ il faut initialiser OFS a "~" ce qui se fait en insérant : OFS="~" avant le print $0

      }' toto Fin du script Awk + nom dui fichier a traiter.

      Suopprime le premier print $0 si tu ne veux pas tes lignes en 2 exemplaires.

      Je laisse quelqu'un d'autre faire la meme chose en Perl.
      • [^] # Re: en awk:

        Posté par  . Évalué à 1.

        Salut et merci, ca me fait avancer, y avait des trucs que je comprenais pas du type OFS="~" et maintenant j'ai pigé du coup, j'en ai fait un plus simple :
        awk 'BEGIN {FS="~";OFS="~"}
        $12 > 999 {print $0}
        $12 < 1000 {$12=$14;print $0}
        END {}' test > test_res

        Seul ic il me met des ~ a la fin de chaque ligne si elles n'ont pas 12 champs minimum. Comment peut on éviter ca?? je vais essayer ta solution aussi.
        • [^] # Re: en awk:

          Posté par  . Évalué à 2.

          Seul ic il me met des ~ a la fin de chaque ligne si elles n'ont pas 12 champs minimum
          Le nombre de champs est disponible via la variable NF. Je te laisse méditer dessus.
          • [^] # Re: en awk:

            Posté par  . Évalué à 1.

            OK merci. j'ai essayé la commande sed 's/^\(ACT\(~[^~]*\)\{10\}~\)0[^~]*\(~[^~]*~\)\([^~]*\)/\1\4\3\4/' actes_39.fcv.old49 > actes_39.fcv.h
            et ça marche trop bien trop vite... du coup plus de soucis... merci quand même, ca m'a éclairé.
  • # info sed

    Posté par  . Évalué à 2.

    Regarde les exemples ; sed ne se limite pas à la commande 's'...

    Et pourtant, ici, une simple commande 's' suffit :
    sed 's/^\(ACT\(~[^~]*\)\{10\}~\)0[^~]*\(~[^~]*~\)\([^~]*\)/\1\4\3\4/'
    • [^] # Re: info sed

      Posté par  . Évalué à 5.

      j'aime ta définition de "simple" ;)
      • [^] # Re: info sed

        Posté par  . Évalué à 0.

        Oui donc j'ai essayé ta formule et ca marche tres bien, merci. bye. à bientot si on peut résoudre les problèmes si vite...
        • [^] # Re: info sed

          Posté par  . Évalué à 1.

          Par contre je perds 11 octets, et qd je fais un diff entre les deux fichiers il me dit 'Missing newline at the end of file du premier fichier' ... il ne devrait pas y avoir de différence en octets car je remplace 4 octets par 4 octets. Je pense que celui qui n'a pas de fin de fichier c'est le plus petit en taille (celui généré par la commande), pourquoi ne reproduit il pas le fichier de facon exact??
  • # "open + while + split + if + join + print + close" grace a RTFM

    Posté par  (site web personnel) . Évalué à 3.

    apres http://linuxfr.org/forums/26/15370.html pour un compte créé le 8 mars 2006 et n'ayant que 2 entrées dans les forums ...

    voila ta réponse, je te demande au moins de faire un don au site linuxfr qui t'evite un zero a l'ecole ou d'etre lourdé de ton stage ( ce n'est pas compliqué d'ouvrir un livre ).

    ce n'est qu'un gabarit, il manque plein de chose ... mais ca te sauvera la vie ( faut vraiment que cela soit crucial et urgent pour ne pas ouvrir un manuel et prendre le temps de le lire ).

    open( IFILE, "<", "input.txt" );
    open( OFILE, ">", "output.txt" );

    while( <IFILE> ) {
    chomp;
    my @col = split( '~', $_ );

    if ( ( $col[0] eq "ACT" ) && ( $col[12] =~ /^0/ ) ) {
    $col[12] = $col[14];
    }

    print join( '~' , @col ), "\n";
    }

    close( IFILE );
    close( OFILE );
    • [^] # Re: "open + while + split + if + join + print + close" grace a RTFM

      Posté par  . Évalué à -1.

      Merci de ta réponse et surtout de ton humour pour prétendre avoir à faire à un écolier ou un stagiaire. Malheureusement pour toi je n'en suis pas un, passons. Donc, Je te ferais juste remarquer que je ne voulais plus passer par une boucle (pour gain de temps), or toi t'y sautes a pieds joints... tes remarques tu te les gardes donc pour toi et la prochaine lis un peu mieux la demande. Sans vouloir te descendre, les autres réponses en plus d'être correctes, avaient l'avantage d'être plus pertinentes. Merci. Bonne journée.
      • [^] # Re: "open + while + split + if + join + print + close" grace a RTFM

        Posté par  (site web personnel) . Évalué à 4.

        alors si tu ne veux pas de boucle, tu deroule la boucle par contre il faut que tu connaisses d'avance le nombre de lignes du fichier :)

        sinon, tu peux tenter une recursion mais cela reste encore une boucle implicite :

        open( my $ifile, "<", "input.txt" );
        open( OFILE, ">", "output.txt" );

        sub recurs {
        my $h = shift;

        my $a = <$h>;
        chomp;
        my @col = split( '~', $_ );

        if ( ( $col[0] eq "ACT" ) && ( $col[12] =~ /^0/ ) ) {
        $col[12] = $col[14];
        }

        print OFILE join( '~' , @col ), "\n";
        recurs( $h ) unless eof( $h );
        }

        recurs( $ifile );

        close( $ifile );
        close( OFILE );

        Maintenant, explique moi comment traiter un fichier de N lignes sans faire une boucle ? Les instructions SIMD pour des traitements complexes gerant des acces disques n'existent pas encore il me semble mais je me trompe peut etre.

        Penses tu serieusement que cat, grep, awk & co ne font pas de boucle quelque part ?

        quel overhead représente un saut pour ton probleme si tu le resoud dans un langage interprété ? autant résoudre ton probleme en ASM, mais de toute facon je ne vois pas comment ne pas mettre de saut sans derouler.

        cadeau : le source d'une version de cat pour OpenBSD http://mirror.sg.depaul.edu/pub/OpenBSD/src/bin/cat/cat.c

Suivre le flux des commentaires

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