Description

La Boîte à Outils 3 extrait des patrons morpho-syntaxiques à partir des fichiers générés par la BàO 2, TXT comme XML.
Dans les fichiers de sortie de Cordial, les éléments token, lemme et partie du discours sont séparés par une tabulation. Dans ceux de Treetagger, ces mêmes éléments sont entourés de balises data, et leur nature est la valeur de l'attribut type. Les deux formats étant bien structurés, on peut donc y extraire les motifs de manière automatique.
Les motifs recherchés sont NC[^\s]+ ADJ[^\s]+ et NC[^\s]+ PREP NC[^\s]+, soit des NOM ADJ et NOM PREP NOM.

De l'étiquetage produit par Cordial

Les fichiers étiquetés de Cordial, en Latin-9, ont été ré-encodés en UTF-8 avant l'exécution des programmes suivants.

Méthode (1)

Le premier programme est proposé par M. Daube. Il prend en entrée deux arguments : le fichier généré par Cordial et un fichier contenant les motifs à rechercher. L'idée est de lire le document .cnr ligne par ligne, en enregistrant à chaque fois le token et sa catégorie morpho-syntaxique, jusqu'au moment où l'on rencontre une ponctuation forte. Deux listes sont alors obtenues, où les informations relatives à un token dans la première liste apparaîtront au même index dans la deuxième. On accède aux termes lexicalisés par un système de calcul d'index reposant sur le comptage des espaces avant le motif, et dans le motif lui-même.
Le script a toutefois été modifié pour qu'il puisse produire en sortie un fichier pour chaque patron.

#!/usr/bin/perl

use strict;
use warnings;

my $guide = "Couldn't run the program: perl $0 file.cnr file";
die $guide unless @ARGV == 2;

open(my $INPUT, "<:encoding(UTF-8)", $ARGV[0]) or die "Couldn't open: $!";

open(my $PATTERNS, "<:encoding(UTF-8)", $ARGV[1]) or die "Couldn't open: $!";
my @POS_LIST = <$PATTERNS>;
close($PATTERNS);

my @TOKENS = (); # Sauvegarde des tokens
my @TAGS = ();   # Sauvegarde des étiquettes

my $ctr_pos = 0; # Compteur de patrons extraits

# Création des fichiers de sortie pour chaque motif
my %FILE_LIST = ();
foreach my $POS (@POS_LIST) {
    # Génération du nom du fichier
    chomp $POS;
    $POS =~ s/\r//;
    my $pattern = $POS;
    $pattern =~ s/\Q[^\s]+\E//g;
    $pattern =~ s/ /-/g;
    my $file = substr($ARGV[0], 0, -4);
    $file = $file."_".$pattern.".txt";

    open(my $OUTPUT, ">:encoding(UTF-8)", $file) or die "Couldn't open $file: $!";
    close($OUTPUT);

    $FILE_LIST{$POS} = $file;
}

while(my $line = <$INPUT>) {
    next if($line !~ /^[^\t]+\t[^\t]+\t[^\t]+$/);
    chomp $line;
    $line =~ s/\r//;
    if($line !~ /PCTFORTE/) {
        my @list = split(/\t/, $line);
        push(@TOKENS, $list[0]);
        push(@TAGS, $list[2]);
    } else {
        &extract_pos_patterns;
        @TOKENS = ();
        @TAGS = ();
    }
}

print "NOMBRE DE PATRONS EXTRAITS : $ctr_pos";
exit;

# -----------------------------------------------------------------

sub extract_pos_patterns {
    foreach my $POS_line (@POS_LIST) {
        chomp $POS_line;
        $POS_line =~ s/\r//;

        open(my $OUTPUT, ">>:encoding(UTF-8)", $FILE_LIST{$POS_line}) or die "Couldn't open: $!";

        my $TAGS_line = join(" ", @TAGS);
        while($TAGS_line =~ /($POS_line)/g) {
            my $before_match = $`;
            my $extr_from = $before_match =~ tr/ //;
            my $e = $POS_line =~ tr/ //;
            my $extr_to = $extr_from + $e;
            my @RSLT_TOKENS = @TOKENS[$extr_from..$extr_to];

            print $OUTPUT "@RSLT_TOKENS\n";

            $ctr_pos++;
        }

        close($OUTPUT);
    }
}

Méthode (2)

Le deuxième programme est proposé par M. Fleury. Il prend en entrée un argument : le fichier généré par Cordial. Le motif est en quelque sorte écrit « en dur » dans le code. Comme précédemment, le fichier de Cordial est lu une ligne à la fois. Cette fois-ci, on cherche à vérifier si la catégorie mopho-syntaxique présente sur cette ligne (troisième colonne) correspond au premier item du patron de recherche. Si c'est le cas, le deuxième item du motif est recherché dans la ligne suivante, et ainsi de suite. Si tous les items du patron ont été trouvés, les uns après les autres, la séquence de tokens correspondants (première colonne) est récupérée.

#!/usr/bin/perl

use strict;
use warnings;

my $guide = "Couldn't run the program: perl $0 file.cnr";
die $guide unless @ARGV == 1;

open(my $INPUT, "<:encoding(UTF-8)", $ARGV[0]) or die "Couldn't open: $!";
my @DATA = <$INPUT>;
close($INPUT);

my %PATTERN_LIST = (); # Patrons extraits
my $ctr_pos = 0;       # Compteur de patrons extraits

# Le patron cherché ici est du type NOM ADJ
while(my $line = shift(@DATA)) {
    chomp $line;
    $line =~ s/\r//;
    my $sequence = "";
    my $length = 0;

    if($line =~ /^([^\t]+)\t[^\t]+\tNC.*/) {
        my $form = $1;
        $sequence .= $form;
        $length = 1;
        my $next_line = $DATA[0];

        if($next_line =~ /^([^\t]+)\t[^\t]+\tADJ.*/) {
            my $form = $1;
            $sequence .= " ".$form;
            $length = 2;
        }
    }
    if($length == 2) {
        $PATTERN_LIST{$sequence}++;
        $ctr_pos++;
    }
}

# Génération du nom du fichier
my $file = substr($ARGV[0], 0, -4);
$file = $file."_NC-ADJ.txt";

open(my $OUTPUT, ">:encoding(UTF-8)", $file) or die "Couldn't open $file: $!";
foreach my $pattern (sort {$PATTERN_LIST{$b} <=> $PATTERN_LIST{$a}} (keys %PATTERN_LIST)) {
    print $OUTPUT "$PATTERN_LIST{$pattern}\n";
}
close($OUTPUT);

print "NOMBRE DE PATRONS EXTRAITS : $ctr_pos";
exit;

Le programme a ensuite été adapté pour rechercher le motif NOM PREP NOM.

De l'étiquetage produit par TreeTagger

Méthode (1)

Le programme développé à la méthode (2) pour Cordial a été modifié pour pouvoir examiner les fichiers XML cette fois-ci. Il fonctionne à l'identique, seules les expressions régulières ont été changées. Il a aussi fallu faire attention aux lignes vides présentes dans le fichier XML. On ne recherche donc pas dans la ligne suivante à chaque fois, mais dans la ligne encore d'après.

Méthode (2)

Au lieu de Perl, il est aussi possible d'utiliser les requêtes XPath pour pouvoir extraire des séquences. La feuille de style XSLT parcourt le fichier XML à la recherche du patron morpho-syntaxique, en comparant une ligne à ses frères (sœurs ?). Le résultat peut ensuite être généré en ligne de commande, avec xsltproc.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text" encoding="UTF-8" />
    <xsl:template match="/">
        <xsl:apply-templates select="./PARCOURS/FILTRAGE/item/*[self::title or self::description]/document/article/element" />
    </xsl:template>

    <xsl:template match="element">
        <xsl:choose>
            <xsl:when test="(./data[contains(text(),'NOM')]) and (following-sibling::element[1][./data[contains(text(),'ADJ')]])">
                <xsl:value-of select="./data[3]" /><xsl:text> </xsl:text>
            </xsl:when>
            <xsl:when test="(./data[contains(text(),'ADJ')]) and (preceding-sibling::element[1][./data[contains(text(),'NOM')]])">
                <xsl:value-of select="./data[3]" /><xsl:text>&#xa;</xsl:text>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Comparaison des résultats

Après comparaison des résultats de l'extraction depuis les fichiers XML et TXT, on peut constater que l'étiquetage effectué par TreeTagger retourne généralement un plus grand nombre d'entrées, mais elles sont moins précises que Cordial. Par exemple, la préposition « d' » est bien reconnue comme telle par Cordial, mais est collée au nom suivant qui est alors considéré comme un adjectif chez TreeTagger.