menu menu2
1
2
3
4
5

Boîte à Outils 3



Une représentation des sorties XML de BAO 3:

Arborescence

Pour obtenir le résultat ci-dessus, il faut extraire les patrons "NOM ADJ" et "NOM PREP NOM" des sorties XML de BAO 2 par le programme puis les transforme par XSLT en format HTML.

Présentation de BAO 3

Cette étape de BAO 3 est l'extraction de patrons syntaxiques des fichiers obtenus à la BAO2. Il s'agit de repérer les suites “NOM ADJ” et “NOM PREP NOM”. On dispose de plusieurs méthodes:

Traitement des sorties TXT Cordial: 2 méthodes Perl utilisant des listes patrons

Traitement des sorties XML: le module XML::LibXML de Perl

Méthode proposée par Jean-Michel Daube

Cette méthode consiste à lire le fichier étiqueté par Cordial jusqu'à trouver une ponctuation forte. Le programme lit le fichier ligne par ligne (forme, lemme et catégorie syntaxique). En utilisant l'étiquette "PCTFORTE" comme indicateur à la fin d'une phrase. Il traite phrase par phrase, en stockant dans trois tableaux différents (@tokens, @lemmes et @catégories) les informations pour chaque mot de la phrase. Quand on rencontre une "PCTFORTE", on effectue le traitement sur les trois listes, et ensuite on les reinitialise pour traiter la phrase suivante.

En prenant des motifs "NOM ADJ" et "NOM PREP NOM", les formes dans les listes patrons sont:

NC[A-Z]+ ADJ[A-Z]+

NC[A-Z]+ PREP NC[A-Z]+

Le script se trouve ci-dessous:

#!/usr/bin/perl
#Le programme du JMD...
#lecture du fichier cordial et d'un fichier de patrons
#important: tout est en iso
#1è argument: le fichier des patrons morphsyntaxique
open(TERMINO, "<$ARGV[0]");
while (my $terme=<TERMINO>) {
    chomp($terme);
    $terme=~s/ +/ /g;
    $terme=~s/\r//g#troisimè colonne de fichier cordial
    open(CORDIAL, "<$ARGV[1]");
    while ($ligne=<CORDIAL>) {
        chomp ($ligne);
        $ligne=~s/\r//g;
        if ($ligne!~/PCT/){
            my @LISTE=split(/\t/,$ligne);
            #print "PATRON LU $terme: Lingne lue <@LISTE>\n";
            push(@POS,$LISTE[2]);
            push(@TOKEN,$LISTE[0]);
        }
        else {
            #on est arrivé sur un PCT, on va la traiter..
            #print "<@TOKEN>\n";
            #print "<@POS>\n";
            #print "TERME CHERCHE: $terme \n";
            #my $a=<STDIN>;
            #on doit chercher si le "scalaire" $terme qui est dans @POS
            #pour cela on va transformer $terme sous la forme d'une liste
            #pour ensuite faire le match entre la liste TERME et la scalaire POS
            #si "match" on va imprimer...
            my $pos=join(" ",@POS);
            my $token=join(" ",@TOKEN);
            my $cmptdetrouvage=0;
            while ($pos=~/$terme/g){
                $cmptdetrouvage++;
                #print "Youpi, TROUVE $cmptdetrouvage fois \n ";
                #print " En effet: $terme est bien dans $pos!!! \n";
                my $avantlacorrespondance=$`;# pb pb pb 
                #on compte le nb de blanc dans avant..
                #super methode: my $comptagedebalanc=() =$avantlacorrespondance=~/ /g;
                my $comptagedebalancdansterme=0;
                while ($terme=~/ /g){
                    $comptagedebalancdansterme++;
                }
                my $comptagedebalanc=0;
                #print " AVANT : $avantlacorrespondance \n";
                while ($avantlacorrespondance=~/ /g) {
                    $comptagedebalanc++;    
                }
                for (my $i=$comptagedebalanc$i<=$comptagedebalanc
                    +$comptagedebalancdansterme$i++) {
                    print $TOKEN [$i]." ";
                }
                print "\n";
            }
            #ceci fini pour la recherche du match
            # on vide les 2 listes de travail avant de recommencer de les remplir
            @POS=();
            @TOKEN=();
        }
        # body...
    }
    close(CORDIAL);   
}
close(TERMINO);
 


[ TOP ]

Méthode proposée par Serge Fleury

Ce programme parcourt la liste des catégorie et des tokens en parallèle. Si une catégorie correspond au premier catégorie d'un patron, alors il vérifie s’il y a les catégories suivantes correspondant à la suite du patron. Si c'est le cas, il extrait les tokens correspondant aux catégories repérées. Ce programme est aussi le principe de fonctionnement du Trameur.

Bis: Alors, on a manipulé un motif de recherche croisée de patrons NOM ADJ (forme 1) dans la section PATRON de Trameur avec la sortie rubrique POLITIQUE. Il extrait tous les séquences de NOM ADJ. On a fait un graphe avec un motif "\bélections\b" pour visualiser le résultat:


Arborescence

Les patrons dans la liste sont en forme ci-dessous:

NC[A-Z]+ ADJ[A-Z]+

NC[A-Z]+ PREP NC[A-Z]+

Le script complet pour extraire "NOM ADJ" est comme suit:

#/usr/bin/perl
open(FILE,"$ARGV[0]");
#--------------------------------------------
# le patron cherché ici est du type NOM ADJ";
#--------------------------------------------
my @lignes=<FILE>;
close(FILE);
while (@lignes) {
    my $ligne=shift(@lignes);
    #on lit une premiere ligne et on le vire dans la liste
    chomp $ligne;
    my $sequence="";
    my $longueur=0;
    if ( $ligne =~ /^([^\t]+)\t[^\t]+\tNC.*/) {
    #on prend le premier element de la ligne qui contient un nom
     my $forme=$1;
     $sequence.=$forme;
     $longueur=1;
     my $nextligne=$lignes[0];
     if ( $nextligne =~ /^([^\t]+)\t[^\t]+\tADJ.*/) {
         my $forme=$1;
         $sequence.=" ".$forme;
         $longueur=2;
     }
    }
    if ($longueur == 2) {
    print $sequence,"\n";
    }
}
 

Le script complet pour extraire "NOM PREP NOM" est comme suit:

#/usr/bin/perl
open(FILE,"$ARGV[0]");
#--------------------------------------------------
# le patron cherché ici est du type "NOM PREP NOM";
#--------------------------------------------------
my @lignes=<FILE>;
close(FILE);
while (@lignes) {
    my $ligne=shift(@lignes);
    chomp $ligne;
    my $sequence="";
    my $longueur=0;
    if ( $ligne =~ /^([^\t]+)\t[^\t]+\tNC.*/) {
     my $forme=$1;
     $sequence.=$forme;
     $longueur=1;
     my $nextligne=$lignes[0];
     if ( $nextligne =~ /^([^\t]+)\t[^\t]+\tPREP.*/) {
         my $forme=$1;
         $sequence.=" ".$forme;
         $longueur=2;
         my $nextnextlinge=$linges[1];
         if ($nextnextlinge=~ /^([^\t]+)\t[^\t]+\tNC.*/) {
             my $forme=$1;
             $sequence.=" ".$forme;
             $longueur=3;
             }
     }
    }   
    if ($longueur == 3) {
    print $sequence, "\n";
    }
}



[ TOP ]

Méthode d'automation

Comme les deux méthodes ci-dessus ne prennent en entrée qu'un seul fichier à la fois, on peut utiliser "subprocess" de Python à gérer:

subprocess.call(["/path/to/script.pl", argument1, argument2])

Selon la même technique utilisée pour le parcours d'arborescence écrit en Perl (BAO 1, BAO 2), on peut aussi d'automatiser cette tâche, ça commence à peu près:

my $patrons="Extract_patron/";
# Création d'un dossier de sorties pour les patrons 
if (! -e $patrons){ 
    mkdir($patronsor die ("Pb à la création du répertoire : $!"); 
}

Créer le chemin d'entrée, récupérer le variable, puis transferer les fichiers lus dans un tableau:

my $path="$ARGV[0]";
$path=~s/[\/]$//;
my $patron="$ARGV[1]";
opendir(DIR, $pathor die "on ne peut pas ouvrir  $path: $!\n";
my @files=readdir(DIR);
closedir(DIR);

Création d'un fichier de sortie pour tous les fichiers, suppression de ".cnr" des noms de fichiers de sorties, exécution du programme:

foreach my $file (@files){
    next if $file=~/^\.\.?$/;
    my $txt="$patrons".$file.'-'.$patron;
    $txt=~s/\.cnr//
    $file=$path."/".$file;
    system("perl JMD.pl(ou SF) $file $patron > $txt");
} 

Problème soulevé

Concernant l’extraction des patrons, vu que des fois les fichiers finaux sont trop gros pour executer le script fournis par le professeur, celui de monsieur Rachid Belmouhoub par exemple, on a fait appel à un script bash pour les couper en plusieurs fichiers plus petits si bien que le script peut être exécuter sans difficulté, le code comme suit:

#!/bin/bash
linenum=`wc -1 httperr8007.log | '{print $1}'`
n1=1
file=1
while [ $n1 -1t $linenum ]
do
            n2=`expr $n1 + 999`
            sed -n "${n2}p" input.txt > output.txt
            n1='expr $n2 + 1`
done

[ TOP ]

Méthode proposée par R. Belmouhoub

XPATH exprime des chemins via la barre oblique et on peut aussi contenir des prédicats pour imposer des conditions aux noeuds sélectionnés. Cette méthode consiste à intégrer des requêtes XPath dans un programme Perl, via le module XML::XPath.

Lecture du fichier de patrons (correspondant à un motif chaque fois). Pour chaque ligne du fichier, un appel à la procédure &extract_pattern, avec le motif comme argument. La procédure &extract_pattern prend le motif actuel et produit une sortie pour ce motif. La construction du chemin se fait via la procédure &construit_XPath qui prend comme argument la ligne du patron, qu'elle transforme en liste pour pouvoir la parcourir. On a dit que ce programme n'exécute pas bien avec des fichiers gros, mais sous Mac OS, on en a utilisé à traiter "ALAUNE" (rubrique ayant le plus de contenu) qui extrait très rapidement.

    
    # Initialisation du compteur pour la boucle de construction du chemin XPath
    my $i=1;
    while ($i < $#tokens) {
        $search_path.="[following-sibling::element[1][contains
        (data[\@type=\"type\"],\"$tokens[$i]\")]";
        $i++;
    }
    my $search_path_suffix="]";
    
    # on utilise l'opérateur x qui permet de répéter la chaine de caractère à
     sa gauche autant de fois que l'entier de sa droite,
    # soit $i fois $search_path_suffix
    $search_path_suffix=$search_path_suffix x $i;
    
    # le chemin XPath final
    $search_path.="[following-sibling::element[1][contains(data[\@type=\"type\"],
    \"".$tokens[$#tokens]."\")]"
                                .$search_path_suffix;

Ce chemin est récupéré par la procédure &extract_pattern. L'exploration de l'arborescence XML a besoin de la création d'un nouvel objet XML::XPATH et il est possible de récupérer les noeuds qui correspondent au chemin créé en faisant une requête sur cet objet via “findnoedes”.

foreach my $noeud ( @nodes) { #noeud est "element"
# Initialisation du chemin XPath relatif du noeud "data" contenant
# la forme correspondant au premier élément du motif
# Ce chemin est relatif au premier noeud "element" du bloc retourné
# et pointe sur le troisième noeud "data" fils du noeud "element"
# en l'identifiant par la valeur "string" de son attribut "type"
        my $form_xpath="";
        $form_xpath="./data[\@type=\"string\"]";#. est l'endroit de xml
        
# Initialisation du compteur pour la boucle d'éxtraction des formes 
# correspondants aux éléments suivants du motif
        my $following=0;
# Recherche du noeud data contenant la forme correspondant au premier   
# élément du motif au moyen de la fonction "find" qui prend pour arguments:
#   1. le chemin XPath relatif du noeud "data"
#   2. le noeud en cours de traitement dans cette boucle foreach
# la fonction "find" retourne par défaut une liste de noeuds, dans notre 
# cas cette liste ne contient qu'un seul élément que nous récupérons avec 
# la fonction "get_node" enfin nous en imprimons le contenu textuel au 
#moyen de la méthode string_value
        print MATCHFILE $xpc->findvalue($form_xpath,$noeud);
#je suis ˆ element, $noeud pointe ˆ element, dans ce context lˆ, 
#findvalue travers le chemin $form_xpath cherche $noeud, 
#dans $noeud je fais une requete xpath
        
# Boucle d'éxtraction des formes correspondants aux éléments 
#suivants du motif On descend dans chaque noeud element du bloc
while ( $following < $#tokens) {
#Incrémentation du compteur $following de cette boucle d'éxtraction 
#des formes
            $following++;
            
# Construction du chemin XPath relatif du noeud "data" contenant
# la forme correspondant à l'élément suivant du motif
# Notez bien l'utilisation du compteur $folowing tant dans la condition de la 
# boucle ci-dessusque dans la construction du chemin relatif XPath
            my $following_elmt="following-sibling::element[".$following."]";
        #chemin xpath chercher frere de             
            $form_xpath=$following_elmt."/data[\@type=\"string\"]"
#il y a 2 boucles, 1 on trouve NOM ex:p·re puis dans 2e boucle, on traite 
#puis NOM: Pi·re Impression du contenu textuel du noeud data contenant la 
#PEP: de, forme correspondant à l'élément suivant du motif
            print MATCHFILE " ",$xpc->findvalue($form_xpath,$noeud);
# Incrémentation du compteur $following de cette boucle d'éxtraction 
# des formes $following++;
        }
        print MATCHFILE "\n";# on fait retour ˆ ligne
    }
    # Fermeture du fichiers de motifs
    close(MATCHFILE);
} 

[ TOP ]

XSL

Comme dans la BAO 1, BAO 2, on peut visualiser les sorties au format XML, avec une feuille de styles XSLT pour le transformer. Les balises sont celles qui contiennent les tags, on peut définir comme chemins:

NOM ADJ: //element[contains(data[1], ‘NOM')][following-sibling::element[1] [contains(data[1], ‘ADJ')]]

NOM PREP NOM: //element[contains(data[1], 'NOM')][following-sibling::element[1][contains(data[1], 'PRP')]][following-sibling::element[2][contains(data[1], ‘NOM')]]

Une partie de code, les "chemins" sont dans les balises:


Arborescence

Un exemple d'extraction de patrons da la rubrique EUROPE:


Arborescence
[ Visualiser ]


Crée par Chang TAN et Si WU, Plurital 2015