OVH Cloud OVH Cloud

recherche multi tableau

35 réponses
Avatar
Fabrice L.
Bonjour,

je recherche dans un fichier plusieurs mots (il peut y en avoir des
centaines)

mon pb est que ...

while (<F>) {

if ($_ =~ /occurence1/) { ... }
elsif ($_ =~ /occurence2/) { ... }
elsif ($_ =~ /occurence3/) { ... }
elsif ($_ =~ /occurence4/) { ... }
elsif ($_ =~ /occurence5/) { ... }
else { ...}
}
etc...

...devient vite une usine a gaz surtout quand vous rechercher dans un
fichier de plusieurs milliers de lignes une centaine d'occurences
différentes.
J'ai pensé mettre mes différentes occurences dans un tableau mais je ne vois
pas comment rechercher par la suite.
Mon deuxieme souci est le temps d'execution et l'utilisation CPU/ RAM.

Je suis preneur de toutes idées et/ou direction possible.

Merci d'avance

Fabrice

10 réponses

1 2 3 4
Avatar
Julien Plée
regexp :
my @mots = qw{mot1 mot2 mot3 truc bidule};
my $regexp = join("|", @mots);


attention le | coûte cher en mémoire s'il y a bcp de mots ! pensez qu'il
va


rechercher chaque mot caractère par caractère, à chaque position du
texte,


avant de passer au mot suivant.


En mémoire ? Plutôt en temps.

En fait cela dépend des spécifications et de la taille des données... Si
on

veut trouver d'abord le premier mot (n'importe où) et ne chercher les
suivants

que si le premier n'est pas trouvé, la méthode de la regexp ne marche pas.

Sinon, ça marche bien surtout si on fabrique une regexp optimisée en
triant

les mots par racine commune. Il y des méthodes pour faire cela.

On peut aussi améliorer le comportement des regexp en ne compilant
l'expression régulière qu'une seule fois...


Je n'avais pas pensé à ça dans mon exemple :)
Mais je pense que la tâche d'automatisation risque d'être lourde si je tente
d'encapsuler les racines communes à la volée...
Mais peut-être celà serait-il plus facilement faisable en encapsulant les
racines lettres par lettres


Julien



Avatar
Laurent Wacrenier
Fabrice L. écrit:
je recherche dans un fichier plusieurs mots (il peut y en avoir des
centaines)


Met les mots dans un hash et lit le fichier mot par mot, ou ligne par
ligne et découpe les lignes en mots :

my %h = map { $_ => 1 } qw/titi toto tata/;

while(<>) {
print if grep { $h{$_} } split " ";
}

Avatar
Fabrice L.
Bonjour,

J'ai trouvé !
en fait :

for (0..$nombre_de_moteurs)

next if (!$liste_moteurs[$_][0]);
if ($elements[9] =~ /$liste_moteurs[$_][0]/) { $elements[9] =~
/$liste_moteurs[$_][1]s*([^&;]+)/i; $or{$1}++; }
}

et tout fonctionne parfaitement tout betement.

Fabrice


"Fabrice L." a écrit dans le message news:
cb77e6$if2$
Merci à tous pour vos réponses :


Avatar
Antoine Dinimant
En fait cela dépend des spécifications et de la taille des données... Si on
veut trouver d'abord le premier mot (n'importe où) et ne chercher les suivants
que si le premier n'est pas trouvé, la méthode de la regexp ne marche pas.


Si. Les moteurs de regex POSIX testent tous les termes de l'alternative
afin de recherche la plus longue correspondance, mais le moteur de Perl
n'est pas POSIX. Tu peux le vérifier ainsi :

'toto une titi' =~ /(un|une)/ ;
print $1 ;

cf. l'excellentissime Friedl, Maîtrise des expressions régulières, chez
O'Reilly

Avatar
Antoine Dinimant
Il faut maintenant décortiquer la fonction index() pour savoir si elle
construit une Rx ;o)


Elle ne le fait pas. La première des recommandations d'optimisation des
regex, c'est "si vous le pouvez, oubliez-nous et utilisez index".

Avatar
Antoine Dinimant

@liste_moteurs=(
[ "google.be", "q=", "Google Belgique" ],
[ "google.ch", "q=", "Google Suisse" ],
[ "google.fr", "q=", "Google France" ],
[ "google.de", "q=", "Google Allemagne" ],
[ "google.pl", "q=", "Google Pologne" ],
[ "google.ru", "q=", "Google Russie" ],
[ "google.ca", "q=", "Google Canada" ],
[ "google.at", "q=", "Google Autriche" ],
[ "google.ae", "q=", "Google Emirats Arabes" ],
### je coupe il y en a bcp !


je confirme l'impression que me laissait ton premier message : tu n'as
absolument pas besoin de regex pour ça, index suffit largement !

(mais pense à enlever le devant le .)

Avatar
Gaëtan Duchaussois
le Mon, 21 Jun 2004 17:16:48 +0200, Fabrice L. a ressenti le besoin de nous dire:
Bonjour,

je recherche dans un fichier plusieurs mots (il peut y en avoir des
centaines)


sinon en complément aux autres réponses si tu veux un truc vraiment
optimisé tu dois pouvoir implémenter l'algorithme de aho-corasick en
perl, mais je ne sais pas si ça vaut l'investissement...

Je suis preneur de toutes idées et/ou direction possible.


Mon idée est un peu tordu je l'avoue mais ça doit pouvoir être ce qui
mouline le moins... L'algo est celui utilisé par agrep.

--
Gaëtan: page perso: http://gduchaussois.free.fr

Avatar
Paul GABORIT
À (at) Tue, 22 Jun 2004 00:26:54 +0200,
Antoine Dinimant écrivait (wrote):
En fait cela dépend des spécifications et de la taille des données... Si on
veut trouver d'abord le premier mot (n'importe où) et ne chercher les
suivants que si le premier n'est pas trouvé, la méthode de la regexp ne
marche pas.


Si. Les moteurs de regex POSIX testent tous les termes de l'alternative afin
de recherche la plus longue correspondance, mais le moteur de Perl n'est pas
POSIX. Tu peux le vérifier ainsi :

'toto une titi' =~ /(un|une)/ ;
print $1 ;

cf. l'excellentissime Friedl, Maîtrise des expressions régulières, chez
O'Reilly


Votre remarque n'est vrai que pour un ancrage donné (un moteur POSIX
afficherait 'une' alors que perl affiche bien 'un').

Ceci étant, regardez l'exemple suivant :

'court et longueur' =~ m/(longueur|court)/;
print $1;

C'est bien 'court' qui est trouvé en premier : la regex est reconnue au plus
tôt (ceci est vrai que le moteur soit POSIX ou Perl). Le recherche de la
regex la plus longue dans le cas d'une alternative (ce qui est fait en POSIX
et non en Perl) n'a lieu qu'à un ancrage donné.

Donc le code :

while (<>) {
if (m/(longueur|court)/) {
print "$1 -> $_";
}
}

N'est pas exactement équivalent à :

while (<>) {
if (m/longueur/) {
print "longueur -> $_";
} elsif (m/court/) {
print "court -> $_";
}
}

(Si on reçoit une ligne contenant "court" avant "longueur"...)

Si vous voulez en savoir plus sur les regex, il y a effectivement le livre
que vous citez mais aussi toute la documentation qui vient avec Perl. Elle
existe même en français :

perlrequick - <http://www.enstimac.fr/Perl/DocFr/perlrequick.html>
perlretut - <http://www.enstimac.fr/Perl/DocFr/perlretut.html>
perlre - <http://www.enstimac.fr/Perl/DocFr/perlre.html>

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>


Avatar
Paul GABORIT
À (at) Tue, 22 Jun 2004 00:30:50 +0200,
Antoine Dinimant écrivait (wrote):
@liste_moteurs=(
[ "google.be", "q=", "Google Belgique" ],
[ "google.ch", "q=", "Google Suisse" ],
[ "google.fr", "q=", "Google France" ],
[ "google.de", "q=", "Google Allemagne" ],
[ "google.pl", "q=", "Google Pologne" ],
[ "google.ru", "q=", "Google Russie" ],
[ "google.ca", "q=", "Google Canada" ],
[ "google.at", "q=", "Google Autriche" ],
[ "google.ae", "q=", "Google Emirats Arabes" ],
### je coupe il y en a bcp !


je confirme l'impression que me laissait ton premier message : tu n'as
absolument pas besoin de regex pour ça, index suffit largement !

(mais pense à enlever le devant le .)


Sur cette dernière remarque, je suis d'accord. Le . est totalement inutile
dans une chaîne de caractères normales.

En revanche, une regexp pourrait être très efficace à condition de la
construire intelligement (ce qui n'est pas faisable facilement
automatiquement). :

%iso_to_name (
be => "Google Belgique",
ch => "Google Suisse",
fr => "Google France",
de => "Google Allemagne",
# etc.
);

if ($log =~ m/google.(be|ch|fr|de)/) {
print "from $iso_to_name{$1}n";
}

C'est certainement plus efficace que plusieurs appels successifs à index... La
construction est ici facile parce que tout commence par 'google.'. Évidemment,
si il y a d'autre moteurs, l'expression rationnelle (ou régulière) n'est plus
aussi simple. Et la mise en facteur de 'google.' devrait être
automatisé... C'est cela qui n'est pas trivial.

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>


Avatar
Fabrice L.
Évidemment,
si il y a d'autre moteurs, l'expression rationnelle (ou régulière) n'est
plus

aussi simple. Et la mise en facteur de 'google.' devrait être
automatisé... C'est cela qui n'est pas trivial.



Et c'est bien la mon probleme. car si il n'y a pas que google, ca va
coincer.

Par contre, parmi toutes les methodes testées

## script1.pl

@liste_moteurs=(
[ "free.fr/google.pl", "q=", "Free" ],
[ "images.google", "q=", "Google Images" ],
[ "googLE.FR", "q=", "Google France" ],
[ "GOOGLE.FR", "q=", "Google France" ],
[ "64.233.161.104", "q=", "Google France" ],
[ "google.be", "q=", "Google Belgique" ],
[ "google.ch", "q=", "Google Suisse" ],
[ "google.fr", "q=", "Google France" ],
### etc et il n'y a pas que google, environ 200 moteurs
);

open F, "$dir_fichier";
@F = <F>;
close F;

foreach (@F) {
my @elements = split '|', $_;
next if (!$elements[9]);

for (0..$nombre_de_moteurs) {

next if (!$liste_moteurs[$_][0]);

if ($elements[9] =~ /$liste_moteurs[$_][0]/)
$or{$liste_moteurs[$_][2]}++;}
}
}

foreach $moteur ( sort sort_or keys %or) {
next if (!$moteur);
print "$moteur $or{$moteur}<BR>";
}

sub sort_or {
...
####

ce script la est tres lent avec quasi 100% d'utilisation CPU (10/12sec. )
sur un fichier de 18000 lignes ( c vrai que pour chaque ligne il faut tester
les 200 moteurs) alors que ...

## script2.pl

open F, "$dir_fichier";
@F = <F>;
close F;

foreach (@F) {
my @elements = split '|', $_;
next if (!$elements[9]);

if ($elements[9] =~ /free.fr/google.pl/) { $or{'Free'}++; }
elsif ($elements[9] =~ /images.google/) { $or{'Google images'}++; }
elsif ($elements[9] =~ /googLE.FR/) { $or{'Google France'}++; }
elsif ($elements[9] =~ /GOOGLE.FR/) { $or{'Google France'}++; }
elsif ($elements[9] =~ /64.233.161.104/) { $or{'Google France'}++; }
elsif ($elements[9] =~ /google.be/) { $or{'Google Belgique'}++; }
elsif ($elements[9] =~ /google.ch/) { $or{'Google Suisse'}++; }
elsif ($elements[9] =~ /google.fr/) { $or{'Google France'}++; }
### etc...
}

foreach $moteur ( sort sort_or keys %or) {
next if (!$moteur);
print "$moteur $or{$moteur}<BR>";
}

sub sort_or {
...
####

...prends a peine 5 secondes sans occuper le CPU a plein régime...

Par contre je préfere le script1 pour des raisons pratiques , en travaillant
avec une liste de liste je peux travailler ET sur le moteur ET sur les
mots-clés alors qu'avec le script2 c soit l'un soit l'autre...

N'y a t-il pas moyen d'optimiser le script1 ?

Fabrice

1 2 3 4