probleme dans le resultats d'un script qui cherche de mots

probleme dans le resultats d'un script qui cherche de mots - Perl - Programmation

Marsh Posté le 10-05-2016 à 16:42:12    

Bonjour
 
je suis debutant en perl et j'ai de soucis par rapport aux resultats de mon script.
c'est un script qui recherche de mots dans plusieurs textes et chaque textes est identifié par un numero (ex: 2563).  
Après avoir tokenisé les textes j'ai analisé chaque mot (maintenant dans un array) avec un boucle for.  
le fiche de resultats doit contenir le numero identifiant de chaque textes et à coté une decision oui/no (1/0) si les mots cherchés on les a trouvés dans le texte.
 
le script repere tres bien les mots mais affiche mal les resultats:
 
si l'identifiant n° 2563 contient les mots cherchés
dans le fiche de resultats il affiche     2563     0
et le suivant id 2564  avec le resultat du premier (dans ce cas 1):
 
du coup on a 2563    0
                     2564    1
 
J'espère que quelqu un peut m'aider
 
dans tout cas merci!
 

Reply

Marsh Posté le 10-05-2016 à 16:42:12   

Reply

Marsh Posté le 10-05-2016 à 18:31:55    

debut25 a écrit :


J'espère que quelqu un peut m'aider


Avec ces infos difficilement... Faut nous montrer le code (entre balises) et un exemple de texte pour pouvoir reproduire le problème.

Reply

Marsh Posté le 10-05-2016 à 19:23:25    

Les textes sont en italien,
je vous en écris un exemple:
 
162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti" http://video.repubblica.it/edizion [...] 69?ref=twh …
 
avec la premiere partie du script on ouvre un fichier qui contient la tokenisation de textes et chaque mot est mit dans un array (dans ce cas @forme)
"
my $i=0;
my $mot_trouv=0;
 
for ($forme[$i] = 0; $i <= $#forme; $i++){ #on commence le boucle pour la recherche
 
 if ($forme[$i] =~ /^\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/){ # on cherche le numero identifiant
  if ($i <= $#forme){          # et chaque fois qu on le trouve on affiche les resultats oui/no
   if ($mot_trouv ne 0){
    print OUT "\"", $forme[$i], "\"", ",\t\"1\"", "\n";
    }
   else {
    print OUT "\"", $forme[$i], "\"", ",\t\"0\"", "\n";
    }
  $mot_trouv=0;  
  $i++;
  }
 }
 elsif ($forme[$i] eq "Governo" ){ #mot cherché "Governo"
  $mot_trouv++;    
  }  
}
close OUT;
"
les resultats de ce script sont  
 
"192902763032743936", "1"
"195604733296254977", "0"
 
comme on peut voir le premier il est pas affiché et son resultat est affiché à coté du deuxieme  
quel est l'erreur?
merci pour votre disponibilité

Reply

Marsh Posté le 10-05-2016 à 19:43:13    

> for ($forme[$i] = 0; $i <= $#forme; $i++)
1) ça devrait pas être for ($i = 0; $i <= $#forme; $i++) plutôt?
Et je vois pas ce que vient faire ce test
> if ($i <= $#forme)
vu que c'est implicite avec la condition de boucle for ($forme[$i] = 0; $i <= $#forme;...
Et je comprends pas non plus ce test
> if ($mot_trouv ne 0)
vu que tu compares une valeur numérique 0 avec le comparateur de chaînes "ne"  
C'est pas simplement
if ($mot_trouv) { c'est à dire if ($mot_trouv != 0) { que tu veux?
 
2) et ça serait pas plus simple avec un foreach?
foreach (@forme) {  
    if (/^\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/) {  
        ....
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-05-2016 à 20:10:40    

Je vous remercie pour vos conseils,  
en modifiant le script comme suivant:
 
my $i=0;
my $mot_trouv=0;
 
foreach (@forme){  
 if ($forme[$i] =~ /^\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/){ # on cherche le numero identifiant
 
  if ($mot_trouv){
    print OUT "\"", $forme[$i], "\"", ",\t\"1\"", "\n";
    }
   else {
    print OUT "\"", $forme[$i], "\"", ",\t\"0\"", "\n";
    }
  $mot_trouv=0;  
   
   
 }
 elsif ($forme[$i] eq "Governo" ){ #mot cherché "Governo"
  $mot_trouv++;    
  }  
$i++
}
close OUT;
 
 
 
j'ai comme resultats:
 
"162545185920778240", "0"
"192902763032743936", "1"
"195604733296254977", "0"
 
avec le meme probleme que avant
Merci encore

Reply

Marsh Posté le 11-05-2016 à 01:19:08    

(Dans les forum je tutoie - libre à toi bien sûr de faire de même.)

 

Tu peux montrer la première partie du script? (Entre parenthèses, faut toujours donner le script complet pour faciliter la vie aux gens qui veulent bien aider...) Sous quelle forme exactement tu reçois les textes, un texte par ligne? Parce que ça serait bien plus élégant à traiter...

 

Le problème est que quand on rencontre un identifiant (numéro) il ne faut pas imprimer cet identifiant et le résultat mais l'identifiant d'avant (car c'est pour celui-là que le résultat est vrai). J'ai corrigé ton script en gardant la logique du code (et modifié quelque détails en passant), ça fonctionne mais c'est vraiment moche...

 

(Pour la prochaine fois, le code entre balises [ code=perl] script ici [ /code] (sans les espaces) c'est bien plus lisible.)

 
Code :
  1. # à mettre au début de tout script (surtout pour un débutant)!!
  2. use strict;
  3. use warnings FATAL=>'all';
  4.  
  5. ############
  6. #improviser la première partie manquante...
  7. $_=<<END;
  8. 162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
  9. 192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
  10. 195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti
  11. END
  12. s/\n/ /g;
  13. my @forme=split(/ /);
  14. ############
  15.  
  16. my $i=0;
  17. my $mot_trouv=0;
  18. my $ident;
  19.  
  20. foreach my $mot (@forme)
  21. {
  22.    if($mot =~ /^\d{18}$/)
  23.    {
  24.        if(defined($ident))
  25.        {
  26.            if($mot_trouv)
  27.            {
  28.                print "\"$ident\",\t\"1\"\n";
  29.            }
  30.            else
  31.            {
  32.                print "\"$ident\",\t\"0\"\n";
  33.            }
  34.        }
  35.        $mot_trouv=0;
  36.        $ident=$mot;
  37.    }
  38.    
  39.    elsif($mot eq "Governo" )
  40.    {
  41.        $mot_trouv++;    
  42.    }  
  43.    
  44.    $i++;
  45. }
  46. #derniere "ligne"
  47. if($mot_trouv)
  48. {
  49.    print "\"$ident\",\t\"1\"\n";
  50. }
  51. else
  52. {
  53.    print "\"$ident\",\t\"0\"\n";
  54. }
 

Si jamais il est possible de récupérer les articles ligne par ligne voici ma version que je trouve bien plus propre:

Code :
  1. use strict;
  2. use warnings FATAL=>'all';
  3.  
  4. $_=<<END;
  5. 162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
  6. 192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
  7. 195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti
  8. END
  9.  
  10. my @articles=split(/\n/);
  11.  
  12. my $i=0;
  13. my $mot_trouv=0;
  14. my $ident;
  15.  
  16. foreach my $article (@articles)
  17. {
  18.    $mot_trouv=0;
  19.    
  20.    foreach my $mot (split(/\s+/, $article))
  21.    {
  22.        if($mot=~/^\d{18}$/)
  23.        {
  24.            $ident=$mot;
  25.        }
  26.        elsif($mot eq "Governo" )
  27.        {
  28.            $mot_trouv++;    
  29.        }
  30.    }
  31.  
  32.    if($mot_trouv)
  33.    {
  34.        print "\"$ident\",\t\"1\"\n";
  35.    }
  36.    else
  37.    {
  38.        print "\"$ident\",\t\"0\"\n";
  39.    }
  40. }
 

Par contre il reste un problème: Quand on cherche p.ex. "Napolitano" on ne trouvera rien avec ce script - parce que dans le texte c'est marqué "#Napolitano:". Je propose donc de "nettoyer" chaque mot avant la comparaison avec un truc genre $mot=~s/[:!#.;"?,]//g;.


Message édité par rat de combat le 11-05-2016 à 01:26:15
Reply

Marsh Posté le 11-05-2016 à 01:26:37    

Un truc de ce genre devrait faire ce que vous voulez.
 

Code :
  1. my $mot_trouv = 0;
  2. my $id;
  3.  
  4. foreach (@forme) {
  5.  if (/^\d{18}$/) {
  6.    if ($id) { # on imprime pas encore pour la première ligne rencontrée
  7.      print OUT "\"", $id, "\"", ",\t\"", ($mot_trouv?1:0), "\"", "\n";
  8.    }
  9.    $id = $_;
  10.    $mot_trouv = 0;
  11.  }
  12.  elsif (/^Governo$/) {
  13.    $mot_trouv++;
  14.  }
  15. }
  16. if ($id) { # on imprime pour la dernière ligne rencontrée
  17.  print OUT "\"", $id, "\"", ",\t\"", ($mot_trouv?1:0), "\"", "\n";
  18. }
  19. close OUT;


 
EDIT: ah je vois que RdC avait déjà répondu une solution similaire
 
A+,


Message édité par gilou le 11-05-2016 à 01:29:15

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-05-2016 à 01:36:26    

Et en intégrant le préambule de RdC pour tester
 

Code :
  1. use strict;
  2. use warnings FATAL=>'all';
  3.  
  4. $_=<<END;
  5. 162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
  6. 192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
  7. 195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti
  8. END
  9.  
  10. my @forme = split(/\b/);
  11. my $mot_trouv = 0;
  12. my $id;
  13.  
  14. foreach (@forme) {
  15.  if (/^\d{18}$/) {
  16.    if ($id) { # on imprime pas encore pour la première ligne rencontrée
  17.      print "\"", $id, "\"", ",\t\"", ($mot_trouv?1:0), "\"", "\n";
  18.    }
  19.    $id = $_;
  20.    $mot_trouv = 0;
  21.  }
  22.  elsif (/^Grillo$/) {
  23.    $mot_trouv++;
  24.  }
  25. }
  26. if ($id) { # on imprime pour la dernière ligne rencontrée
  27.  print "\"", $id, "\"", ",\t\"", ($mot_trouv?1:0), "\"", "\n";
  28. }


 

"162545185920778240",   "0"
"192902763032743936",   "1"
"195604733296254977",   "1"


 

Citation :

Par contre il reste un problème: Quand on cherche p.ex. "Napolitano" on ne trouvera rien avec ce script - parce que dans le texte c'est marqué "#Napolitano:". Je propose donc de "nettoyer" chaque mot avant la comparaison avec un truc genre $mot=~s/[:!#.;"?,]//g;.


d'ou mon split sur les frontières de mot, avec \b (qui marche pas avec les mots composés je crois mais permet néanmoins de faire un rapide test ici)
 
A+,

Message cité 1 fois
Message édité par gilou le 11-05-2016 à 01:41:45

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-05-2016 à 01:44:49    

Citation :

EDIT: ah je vois que RdC avait déjà répondu une solution similaire


J'ai été plus rapide mais tu as fait plus élégant. :o

 
gilou a écrit :


d'ou mon split sur les frontières de mot, avec \b (qui marche pas avec les mots composés je crois mais permet néanmoins de faire un rapide test ici)


Merci, je ne connaissais pas.

 

edit: L'utilisation de \b introduit un comportement qui peut être un bug: On retrouve dans la liste des mots aussi les mots qui se trouvent dans d'éventuells liens - autrement dit si j'ai dans le premier article un lien avec une adresse genre http://Grillo.Machin.Chose.it j'aurai $mot_trouv=1. A voir si c'est un problème (je dis ça si le TO veut réutiliser \b).


Message édité par rat de combat le 11-05-2016 à 01:58:35
Reply

Marsh Posté le 11-05-2016 à 02:32:48    

> L'utilisation de \b introduit un comportement qui peut être un bug...
Ce pourquoi j'ai dit que c'était utile pour une procédure de test rapide. Mais comme il a déjà écrit un tokenizer, ça n'a pas d'importance pour lui, il suffit qu'il le réutilise pour construire sa liste de tokens.
 
De toute façon, ce n'est pas ainsi qu'il faut traiter ce type de problème si on veut être efficace et que l'on a pas mal de lignes:
Il faut faire un hash dont les clés sont les mots et les valeurs, un array contenant les numéros de ligne contenant le mot.
Ainsi, tout est fait en une fois et on ne rescanne pas toutes les lignes à chaque recherche de mot.
 

Code :
  1. use strict;
  2. use warnings FATAL=>'all';
  3.  
  4. $_=<<END;
  5. 162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
  6. 192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
  7. 195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti
  8. END
  9.  
  10. my %word;
  11. my @ids;
  12. my $id;
  13. foreach (split(/\b/)) {
  14.  if (/^\d{18}$/) {
  15.    $id = $_;
  16.    push @ids, $_;
  17.  }
  18.  elsif ($word{$_} && ($word{$_}->[-1] != $id)) {
  19.    push @{$word{$_}}, $id;
  20.  }
  21.  else {
  22.    $word{$_} = [$id];
  23.  }
  24. }
  25.  
  26. sub printlines {
  27.  my $searched = shift;
  28.  print "$searched:\n";
  29.  if ($word{$searched}) {
  30.    my $max = @{$word{$searched}} + 0;
  31.    my $i = 0;
  32.    foreach (@ids) {
  33.      if (($i < $max) && ($word{$searched}->[$i] == $_)) {
  34.         print "\"", $_, "\"", ",\t\"", 1, "\"", "\n";
  35.         ++$i;
  36.      }
  37.      else {
  38.         print "\"", $_, "\"", ",\t\"", 0, "\"", "\n";
  39.      }
  40.    }
  41.  }
  42.  else {
  43.    foreach (@ids) {
  44.      print "\"", $_, "\"", ",\t\"", 0, "\"", "\n";
  45.    }
  46.  }
  47.  print "\n";
  48. }
  49.  
  50. printlines("Napolitano" );
  51. printlines("Grillo" );
  52. printlines("Governo" );


Napolitano:
"162545185920778240",   "0"
"192902763032743936",   "0"
"195604733296254977",   "1"
 
Grillo:
"162545185920778240",   "0"
"192902763032743936",   "1"
"195604733296254977",   "1"
 
Governo:
"162545185920778240",   "1"
"192902763032743936",   "0"
"195604733296254977",   "0"


 
On peut simplifier l'écriture avec les smartmatches:

Code :
  1. sub printlines {
  2. no warnings 'experimental::smartmatch';
  3.  my $searched = shift;
  4.  print "$searched:\n";
  5.  if ($word{$searched}) {
  6.    foreach (@ids) {
  7.         print "\"", $_, "\"", ",\t\"", ($_ ~~ @{$word{$searched}})?1:0, "\"", "\n";
  8.    }
  9.  }
  10.  else {
  11.    foreach (@ids) {
  12.      print "\"", $_, "\"", ",\t\"", 0, "\"", "\n";
  13.    }
  14.  }
  15.  print "\n";
  16. }


mais je ne suis pas sur que ce soit plus efficace.
 
A+,


Message édité par gilou le 11-05-2016 à 04:04:10

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-05-2016 à 02:32:48   

Reply

Marsh Posté le 11-05-2016 à 09:40:03    

je vous remercie pour vos conseils! et pour tous les infos! ça m'aide beaucoup!!! mercie encore  et bonne journée!

Reply

Marsh Posté le 11-05-2016 à 15:53:19    

Noter que pour le tokenizer, comme on a des mots avec accents (qui plaisent pas au split /\b/ on peut faire
foreach (split(/[^\p{L}\p{N}'-]+/)) {
On splitte sur tout ce qui n'est pas une lettre, un chiffre (tous deux, au sens unicode), ' ou -.
On peut même améliorer avec
use Lingua::StopWords qw( getStopWords );
my $stopwords = getStopWords('it');
foreach (grep(!$stopwords->{lc($_)}, split(/[^\p{L}\p{N}'-]+/))) {
si on veut éliminer les mots sans importance les plus courants (ça vire "perché" et "E" ici par exemple)
 
Bref, un  
my $text =<<END;
162545185920778240 Governo Monti: decreto in cdm per approvazione! http://fb.me/1bj50bZI9  
192902763032743936 #Ferrara critica #Grillo perché dice cose che dicevano Berlusconi e Bossi. E che non hanno fatto.
195604733296254977 #Grillo contro #Napolitano: "Presidente della Repubblica? No, presidente dei partiti
END
$text =~ s#(https?://\S+?)(\.?\s)##igs;
foreach (grep(!$stopwords->{lc($_)}, split(/[^\p{L}\p{N}'-]+/, $text))) {
....
ça tokenize comme suit
162545185920778240
Governo
Monti
decreto
cdm
approvazione
192902763032743936
Ferrara
critica
Grillo
dice
cose
dicevano
Berlusconi
Bossi
fatto
195604733296254977
Grillo
Napolitano
Presidente
Repubblica
No
presidente
partiti
 
ce qui me semble assez clean, après, on peut affiner au vu de son input.
 
A+,


Message édité par gilou le 11-05-2016 à 16:02:13

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed