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
Antoine Dinimant
Bonjour,

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


si 1/ tu veux juste tester la présence d'au moins l'un des mots et 2/
tes mots sont fixes et n'ont pas besoin d'être dans une regex :

while (<F>) {
foreach my $mot (@mots) {
if (index($_, $mot) > -1 {...; last ;}
}
}

si par contre tu veux récupérer les lignes contenant l'un de tes mots,
regarde plutôt du côté de grep.

au passage, il me semble que tu utilises à tort le terme "occurences"
pour désigner les mots que tu recherches, qui seraient plutôt des
critères. Une occurence, c'est la survenue d'un de ces mots dans ton
texte ; ainsi si ton mot apparaît trois fois, on dit qu'il a trois
occurences, chacune ayant sa position, son contexte, etc.

De même, j'aurais rédiger mon 1/ en "tu veux juste tester l'occurence
d'au moins l'un des mots".

Antoun

Avatar
Paul GABORIT
À (at) Mon, 21 Jun 2004 17:16:48 +0200,
"Fabrice L." écrivait (wrote):
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.


Vous pouvez placez vos mors dans un tableaux :

my @mots = qw{mot1 mot2 mot3 truc bidule};

while (<F>) {
MOT: foreach my $mot (@mots) {
if ($_ =~ m/$mot/) {
...
last MOT;
}
}
}

Vosu pouvez aussi construire une regexp :

my @mots = qw{mot1 mot2 mot3 truc bidule};

my $regexp = join("|", @mots);

while (<F>) {
if ($_ = m/($regexp)/) {
print "premier mot reconnu: $1n";
...
}
}

Bien sûr, ces exemples simples ne fonctionnent que si vos mots ne contiennent
que des caractères sympathiques...

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

Avatar
Antoine Dinimant
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.

Avatar
Julien Plée
Plusieurs solutions s'offrent à vous suivant justement La direction que vous
devez suivre... Certes, des schémas plus ou moins identiques peuvent
s'appliquer, cependant il est nécessaire de savoir précisément ce dont vous
avez besoin.

Ainsi, je me propose de vous présenter quelques solutions envisageables
suivant quelques besoins différents (du plus simple au plus complexe).

### 1er cas : en rapport avec des besoins très limités.
# Vous disposez d'une liste d'occurences dans un fichier
# ou un utilisateur vous en soumet une.
# Vous placez cette liste dans un tableau @occs.
# Aussi, on suppose que vous n'avez pas besoin de connaître
# si toutes les occurences possibles sont présentes, votre seul
# intérêt est de savoir si au moins l'une d'elle est présente.

my @occs = <OCC_FH>;
my $occs = join '|', @occs;
while (<F>) {
if ($_ =~ m/$occs/) {...}
}


### 2eme cas : un peu plus évolué, un peu plus lent
# En reprennant la proposition ci-dessus, on souhaite
# maintenant pouvoir associer une même action à chaque
# occurence existante (donc pas une seule action par ligne)
# On doit pour cette action récupérer le terme de l'occurence
# rencontrée (dans le cas où une occurence simple est
# présente dans une occurence plus complexe, il faut penser à
# placer l'occurence complexe avant l'occurence plus simple ;
# ex : ['bon', 'bonjour'] ne se comportera pas correctement
# ['bonjour', 'bon'] adoptera le comportement prévu

my @occs = <OCC_FH>;
my $occs = join '|', @occs;
while (my $ligne = <F>) {
pos $ligne = 0;
while ($ligne =~ m/($occs)/g) {
faire_qqch_avec($1);
};
};

### 3eme cas : encore un peu plus évolué, sûrement beaucoup plus lent :)
# En reprennant encore une fois la proposition précédente (2eme),
# on décide d'assigner une action différente en fonction de l'occurence
# rencontrée, et c'est maintenant que les choses se gâtent, car non
seulement
# ils existe plusieurs manières différentes d'arriver au résultat voulu,
mais le choix
# dépendra de la manière dont vous souhaitez mettre à disposition vos
fonctions
# pour votre script.
# Si vous devez utiliser des fonctions limitées, alors je pense que le mieux
serait
# de simuler un tableau associatif dans votre fichier d'occurence :
# occ1: action1; action2; action3;
# occ2: action4; action5; action6;
# <etc...>
#
# de cette manière, on charge le contenu du fichier dans un tableau
associatif
# ainsi :
# my %occs;
# while (<OCC_FH>) {
# $_ =~ m/^([^:]+):(.*)/;
# $occs{$1} = $2;
# };
#
# Voici donc le code au complet :


my %occs;
while (<OCC_FH>) {
$occs{$1} = $2 if ( $_ =~ m/^([^:]+):(.*)/ );
};
my $occs = join '|', reverse sort keys %occs;
# avec le "reverse sort", on n'a plus besoin de faire attention
# à la position de l'occurence dans le fichier
while (my $ligne = <F>) {
pos $ligne = 0;
while ( $ligne =~ m/($occs)/g ) {
eval( $occs{$1} );
};
};



Voilà, ensuite, on peut faire des code plus complexe ou peut-être plus
optimisés, mais ca me semble déjà pas mal au niveau du rapport
compromis/performances
celà évite les if et autres conditions ou simulation de switch.
Note : le modifieur "g" appliqué à la regex sert à tester la regex sur la
chaine depuis la fin de la dernière occurence trouvée. Ainsi, on ne répète
pas indéfiniment la condition qui serai toujours vraie si on rencontrais
l'occurence au moins une fois (pis ensuite, impossible de sorti de la
condition à moins d'un "last" ou "retrun".


Voilà, j'espère qu'il y a tout ce que vous attendiez, n'hésitez pas à poser
des questions ou préciser votre cas pour ajuster le code (qui, je ferais
sûrement bien de le précisé, n'a pas été testé et comporte peut-être une
erreur quelque part ^^)


Julien
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.


ca coute moins cher pour le proc qu'un foreach avec une reconstruction
redondante de la regex ;)
si on veux, on peut aussi découper le tableau en mini tableaux qui seront
testés successivement, mais on se risque à des comportements étranges en
fonction des attentes...


Avatar
Antoine Dinimant
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.



ca coute moins cher pour le proc qu'un foreach avec une reconstruction
redondante de la regex ;)


yes, mais je suggérais d'utiliser index > -1 plutôt qu'une regex...


Avatar
Julien Plée
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.



ca coute moins cher pour le proc qu'un foreach avec une reconstruction
redondante de la regex ;)


yes, mais je suggérais d'utiliser index > -1 plutôt qu'une regex...


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



Avatar
Paul GABORIT
À (at) Mon, 21 Jun 2004 18:16:32 +0200,
Antoine Dinimant écrivait (wrote):
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...

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


Avatar
Fabrice L.
Merci à tous pour vos réponses :

J'ai avancé dans mon histoire en utilisant une liste de liste puis une
boucle ...
C peut etre pas très catégorique mais ca fonctionne et c'est assez rapide.

Cependant j'ai un autre souci, qui n'a rien a voir avec mon premier pb.

Voici le code complet :
(ce script me permet de connaitre l'utilisation du moteur google et les mots
clés liés par les visiteurs de mon site)

Mon pb vient de l'extraction des mots clés.

#!/usr/bin/perl

print "Content-type: text/htmlnn";

@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 !
);

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

$nombre_de_moteurs = @liste_moteurs;

foreach (@F) {
my @elements = split '|', $_;

for (0..$nombre_de_moteurs) { ### j'ai pas trouvé mieux pour tester tous
les moteurs et malgré tout c assez, voire très rapide

## ici j'extrait tous les mots clés
if (($elements[9] =~ /$liste_moteurs[$_][0]/) { $elements[9] =~
/$liste_moteurs[$_][1]s*([^&;]+)/i; $or{$1}++; } ## fonctionne
bizarrement...

## mais
if (($elements[9] =~ /$liste_moteurs[$_][0]/) { $elements[9] =~
/q=s*([^&;]+)/i; $or{$1}++; } ## fonctionne parfaitement... pourtant
$liste_moteurs[$_][1] est bien égal à 'q='
}
}

foreach $outil ( sort sort_or keys %or) {
print "$outil $or{$outil}<BR>";
}

sub sort_or {
etc...


Il y a certainement une subtilité qui m'echappe mais je ne vois evidemment
pas laquelle sinon je ne serai pas en train de vous ecrire :)

merci d'avance pour vos illuminations...
Avatar
Fabrice L.
if (($elements[9] =~ /$liste_moteurs[$_][0]/) { $elements[9] =~
/$liste_moteurs[$_][1]s*([^&;]+)/i; $or{$1}++; } ## fonctionne
bizarrement...


double erreur typo il faut lire if ($element... et non if (($element...

## mais
if (($elements[9] =~ /$liste_moteurs[$_][0]/) { $elements[9] =~
/q=s*([^&;]+)/i; $or{$1}++; } ## fonctionne parfaitement... pourtant
$liste_moteurs[$_][1] est bien égal à 'q='
}
}

foreach $outil ( sort sort_or keys %or) {
print "$outil $or{$outil}<BR>";
}

sub sort_or {
etc...


Il y a certainement une subtilité qui m'echappe mais je ne vois evidemment
pas laquelle sinon je ne serai pas en train de vous ecrire :)

merci d'avance pour vos illuminations...







1 2 3 4