IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

parcours rapide de deux fichiers texte et comparaison ligne par ligne


Sujet :

Langage Perl

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Mars 2010
    Messages
    157
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 157
    Par défaut parcours rapide de deux fichiers texte et comparaison ligne par ligne
    Bonjour

    j'ai un code qui permet de lire deux fichier ligne par ligne et de comparer ces lignes une par une et retourner les lignes qui contiennent un certain nombre de mots en commun

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #!/usr/bin/perl
    use strict;
    my $t0 = time;
    my $i;
    my $file1 = $ARGV[0] || 'trans3';
    my $file2 = $ARGV[1] || 'gh3-3.n';
     
    my %uniq1=();
    my $count1=0; my $words1=0;
    open FICC,'<',$file1 or die "$file1 : $!";
    while (<FICC>) {
      my @words = split /\s+/,lc $_;
      ++$uniq1{$_} for @words;
      $words1 += @words;
      ++$count1;
    }
    close FICC;
    my $uniq1 = scalar keys %uniq1;
     
    my %uniq2=();
    my $count2=0; my $words2=0;
    open FIC,'<',$file2 or die "$file2 : $!";
    while (my $line = <FIC>) {
      my @words = split /\s+/,lc $line;
      ++$uniq2{$_} for @words;
      $words2 += @words;
      ++$count2;
      my @match = grep $uniq1{$_}, @words;
     
     
     
    print  $line if @match >= 2;
     
    }
    close FICC;
    Le problème c'est que je veux afficher le numéro de la ligne du premier fichier qui a été retrouvé dans le fichier n°2.
    Aussi il y a des parties du code que je comprends pas:

    que contient $uniq1 et $uniq1{$_}?

    parce cette instruction n'est pas trop claie pour moi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     my @match = grep $uniq1{$_}, @words;
    ce que je comprends qu'il cherche le contenu de $uniq1{$_} dans @words! mais comment il acède à $uniq1 sans un foreach ou sans indice

    Merci

  2. #2
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Par défaut
    Tel qu'il est écrit, le script ne devrait pas marcher.

    L'idée du script est :
    - de lire le fichier 1, et pour chaque ligne, créer un hash (%hash1) avec pour clé chaque mot de la ligne
    - de lire le fichier 2, et pour chaque ligne, créer un hash (%hash2) avec pour clé chaque mot de la ligne et de vérifier si des mots de (keys %hash2) existent dnas %hash1.

    Pour bien réaliser l'opération, cependant :
    - il faudrait lire les deux fichiers en parallèle, une ligne après l'autre (ici, on lit totalement le fichier 1, et %hash1 contient alors tous les mots du fichier et non seulement ceux d'une ligne en particulier)
    - quand on parcours le fichier 2, il n'est pas nécessaire de stocker les mots dans un hash, il suffit juste de les tester dans le hash des mots de la ligne courante du fichier un.

    Voici un exemple (je ne l'ai pas testé, ni même compilé) de ce qu'il faudrait faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    #!/usr/bin/perl
    use strict;
     
    my $file1 = $ARGV[0] || 'trans3';
    my $file2 = $ARGV[1] || 'gh3-3.n';
    open my $FIC1, "<", $file1 or die "Can't open $file1 for reading: $!";
    open my $FIC2, "<", $file2 or die "Can't open $file2 for reading: $!";
    while (defined(my $line1 = <$FIC1>)) {
      chomp($line1);
      chomp(my $line2 = <$FIC2>);
      my %line1;
      $words1{$_}++ foreach split /\s+/, $line1;
      my @match = grep $words1{$_}, split /\s+/, $line2;
      print "Ligne $. : $line1 (", scalar(@match), " words in commun)\n" if @match > 2;
    }

  3. #3
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par africanwinners Voir le message
    Aussi il y a des parties du code que je comprends pas:

    que contient $uniq1 et $uniq1{$_}?

    parce cette instruction n'est pas trop claie pour moi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     my @match = grep $uniq1{$_}, @words;
    ce que je comprends qu'il cherche le contenu de $uniq1{$_} dans @words! mais comment il accède à $uniq1 sans un foreach ou sans indice
    Une instruction grep fonctionne essentiellement de la même façon que l'instruction map que je t'ai expliquée il y a deux jours: elle prend en entrée la tableau situé à sa droite et traite un par un chaque élément du tableau (qui prend provisoirement la valeur $_). La différence par rapport à map est qu'un grep renvoie les éléments du tableau en entrée pour lesquels l'expression du grep est vraie. Ici, le grep vérifie pour chaque élément du tableau @words si ce mot existe (et est vrai) dans le hachage %unique1 et renvoie dans le tableau @match les éléments satisfaisant cette condition. Il y a donc bien une boucle implicite. En fait, cette ligne de code pourrait être réécrite avec un foreach comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    my @match;
    foreach (@words) {
        push @match, $_ if $uniq1{$_};
    }
    Mais tout cela est expliqué dans le tutoriel que je t'ai déjà conseillé dans ta question sur le map. Je ne peux que te conseiller une nouvelle fois de le lire.

    Sur ta question plus générale, le code que tu as peut à mon avis marcher et même te donner le numéro de ligne que tu recherches moyennant un petit changement mineur à une condition: que les mots dans le fichier 1 soient tous uniques (pas de doublons). Si ces mots peuvent apparaître plusieurs fois dans le fichier 1, alors il faut que tu précises comment on détermine le numéro de ligne du premier fichier que tu veux afficher, puisqu'il peut y en avoir plusieurs. S'ils sont uniques, je pourrai te donner le bout de code qu'il me semble nécessaire de modifier.

  4. #4
    Membre confirmé
    Inscrit en
    Mars 2010
    Messages
    157
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 157
    Par défaut
    Merci à vous deux.
    Je comprends mieux ce code.
    y a t'il pas un moyen qui me permet de comparer les mots ligne par ligne. si je retrouve un certain nombre de mots en commun alors je retourne cette ligne (par exemple pour des lignes qui contiennent 5 mots chacune, si je trouve 3 mots, on peut dire les lignes sont similaires)

    Je le fais déja en utilisant un split et deux boucles (lecture par ligne du fichier 1, et lecture par ligne du fichier 2)
    Mais le problème c'est un code qui prends un temps énorme, des jours peut être des semaines

    voila mon code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    #!/usr/bin/perl
    use strict;
    use warnings;
     
    my $file1 = <<FILE;
    chirac prime paris
    chirac prime jacques
    chirac prime president 
    chirac paris france
    chirac paris french
    FILE
    my $file2 = <<FILE;
    chirac presidential migration 
    chirac presidential paris 
    chirac prime president
    chirac presidential 007
    chirac paris migration 
    chirac paris french
    FILE
     
    #open my $inA, '<', $ARGV[0] or die "Can;t open $ARGV[0]: $!\n";
    #open my $inB, '<', $ARGV[1] or die "Can;t open $ARGV[0]: $!\n";
    open my $inA, '<', \$file1;
    open my $inB, '<', \$file2;
     
    #print "bonjour\n";
    #print "choose the output file name\n";
    #
    #chomp(my $fic2 = <STDIN>);
    #open my $outFile, '>', $fic2 or die "Can't create $fic2: $!\n";
     
    my @aLines;
     
    while (my $ligne = <$inA>) {
        chomp $ligne;
        push @aLines, lc($ligne);
    }
     
    while (my $che = <$inB>) {
        chomp $che;
     
        my @bWords = split(/\s/, $che);
     
        foreach my $kh (@aLines) {
            my @aWords = split(/\s/, $kh);
            my $total = 0;
     
            for my $bWord (@bWords) {
                my $matched;
     
                for my $aWord (@aWords) {
                    $matched = $bWord eq $aWord;
                    last if $matched;
                }
     
                $total++ if $matched;
            }
     
            #print the retrieved line
            #print $outFile "$.: $kh\n";
           if ($total>1)
     {print "$.: $kh\n";}
        }
    }
    le problème avec ce code c'est la longuer du temps de traitement, si je trouve un autre moyen par exemple utiliser un hash de array

  5. #5
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Par défaut
    La méthode que je t'ai proposée (lire les deux fichiers en même temps dans la même boucle, ligne à ligne) est à mon sens la solution la moins consommatrice de ressources (que ce soit CPU ou mémoire ; d'ailleurs, une surconsommation de mémoire peut entrainer de forts ralentissements du au swap par exemple).

  6. #6
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Si tes fichiers ont la même structure (mêmes nombres de lignes, lignes équivalentes), alors tu peux lire tes deux fichiers en parallèle et gagner beaucoup de temps. Mais cela ne marche pas si une seule ligne excédentaire ou manquante sur l'un des fichiers entraîne un décalage entre les deux lectures.

    S'ils n'ont pas la même structure, alors tu n'as pas trop le choix, il faut des boucles imbriquées et comparer chaque ligne du fichier A avec chaque ligne du fichier B.

    Tu peux toutefois accélérer le processus en faisant les choses suivantes:
    - Splitter les lignes du fichier A dès la première lecture du fichier A; cela permet de ne faire le split de chaque ligne du fichier A qu'une seule fois, au lieu de le faire pour chaque ligne du fichier B;
    - il y a peut-être moyen d'utiliser un hachage pour stocker les données du fichier A; une recherche de mots dans un hachage est bien plus rapide que le parcours des éléments d'un tableau.

    Précise bien les conditions de départ (structure du fichier) et aussi ce que tu désires obtenir exactement, il est très possible (et même probable) que l'on puisse te proposer un moyen d'accélérer notablement tes recherches.

    Une autre optimisation possible est de sortir de ta boucle intérieure dès que tu as atteint la condition recherchée (nombre de correspondances > 1 dans ton code), au lieu de continuer à rechercher d'autres correspondances alors que tu n'en feras rien.

Discussions similaires

  1. [Débutant] Comparaisons entre deux fichiers textes
    Par shootgirl dans le forum MATLAB
    Réponses: 9
    Dernier message: 27/09/2011, 14h50
  2. Comparaison de cellules ligne par ligne
    Par Theka dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 08/07/2011, 09h16
  3. Comparaison de deux fichiers texte
    Par Imad_ing dans le forum Autres Logiciels
    Réponses: 0
    Dernier message: 14/10/2010, 16h54
  4. comparaison de deux fichiers textes avec un script python
    Par zekruss dans le forum Général Python
    Réponses: 1
    Dernier message: 10/11/2009, 05h52
  5. comparaison de deux fichiers textes
    Par chmaichel dans le forum Delphi
    Réponses: 1
    Dernier message: 28/07/2006, 11h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo