Comment maintenir le contenu d'une variable?

Résolu
artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024 - 19 août 2023 à 15:34
artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024 - 30 août 2023 à 06:50

Bonjour,

  Je fais le traitement de fichiers en plusieurs étapes. J'ai réussi, jusqu'à maintenant, toutes les étapes, sauf une. J'ai rédigé un programme mais ça ne fonctionne pas.

  Le programme doit lire chaque ligne d'un fichier et réécrire correctement la ou les lignes dans un fichier de sortie.

  Le fichier d'entrée contient, à chaque ligne, un mot. Les mots se classent en cinq différentes catégories. Chaque catégorie est désignée par une lettre :

  A - adjectifs
  B - adverbes
  V - verbes
  X - autres catégories
  N - les noms

  A la fin de chaque mot, il y a un trait d'union. Après le trait d'union, il y a trois possibilités :

  1) soit une lettre majuscule (A, B, V ou X)
  2) plus d'une lettre majuscule
      Exemples :
      - AN  : le mot est à la fois un adjectif et un nom
      - ABN : le mot est à la fois un adjectif, un adverbe et un nom
  3) ou rien pour un nom (je n'écris pas le N pour les noms sauf si le mot appartient aussi à une autre catégorie).

Voici un exemple d'un petit fichier d'entrée (entree.txt) :

accrocher-V
aéronef-
agile-A
bien-ABN
ciel-
éducatrice-AN
manger-NV

Le programme devrait générer le fichier de sortie suivant  (sortie.txt) :

accrocher-V
aéronef-
agile-A
bien-A
bien-B
bien-
ciel-
éducatrice-A
éducatrice-
manger-
manger-V

Ce n'est pas le cas. Voici le résultat de mon programme :

accrocher-V
-V
aéronef-
agile-A
-A
bien-ABN
-A
ciel-
éducatrice-AN
-A
manger-NV
-
-V

Mon programme est :


#  !/usr/bin/perl

use strict;
use warnings;

# Ouverture des fichiers d'entrée et de sortie

open(FICHIER_ENTREE, "entree.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_SORTIE, ">sortie.txt") or die "Le fichier ne s'ouvre pas: $!";


# Début du traitement

while(<FICHIER_ENTREE>)
{
 if (/^([a-zàâçéèêëîïôùû]+[\*\-\']*[a-zàâçéèêëîïôùû]*[\*\-\']*[a-zàâçéèêëîïôùû]*)(\s*)-(A|ABN|AN|V|NV|\n)$/)
 {
  my $x = "$_";
  print FICHIER_SORTIE "$x";

  if ($3=~'A')
  {
   print FICHIER_SORTIE "-A\n";
  }
  else
  {
   if ($3=~'ABN')
   {
    print FICHIER_SORTIE "-A\n";
    print FICHIER_SORTIE "-B\n";
    print FICHIER_SORTIE "-\n";
   }
   else
   {
    if ($3=~'AN')
    {
     print FICHIER_SORTIE "-A\n";
     print FICHIER_SORTIE "-\n";
    }
    else
    {
     if ($3=~'NV')
     {
      print FICHIER_SORTIE "-\n";
      print FICHIER_SORTIE "-V\n";
     }
     else
     {
      if ($3=~'V')
      {
       print FICHIER_SORTIE "-V\n";
      }
     }
    }
   }
  }
 }
}  # WHILE

close FICHIER_ENTREE;
close FICHIER_SORTIE;


Le problème concerne la variable $1. Dans le test suivant :

if (/^([a-zàâçéèêëîïôùû]+[\*\-\']*[a-zàâçéèêëîïôùû]*[\*\-\']*[a-zàâçéèêëîïôùû]*)(\s*)-(A|ABN|AN|V|NV|\n)$/)

$1 correspond au mot mais quand je fais d'autres tests comme, par exemple :

if ($3=~'A')

$1 devient A.

Comment garder le mot dans une variable lorsqu'on fait d'autres tests?

Merci

2 réponses

blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024 3 289
Modifié le 22 août 2023 à 14:32

Salut,

tu peux, si toutes les lignes contiennent un -, faire un split et mettre ça dans un tableau. Ca évite de gérer avec des regex (mais attention aux noms composés : haut-parleur, par exemple)

@TMP = split(/-/,$LIGNE_LUE);

Ensuite, dans $TMP[0], tu as le nom et dans $TMP[1], tu as les attributs.

Tu peux ensuite splitter les attributs (avec un séparateur vide, donc chaque caractère) et boucler sur le nouveau tableau créé.

Ca peut donner ça (je n'ai pas fait la boucle de lecture) :

#!/usr/bin/perl
$mot = "bien-AN";
@TMP = split(/-/,$mot);
$mot = $TMP[0];
@attribs = split(//,$TMP[1]);
foreach my $attrib (@attribs)
{
    print $mot."-".$attrib."\n";
}

Reste à gérer les exceptions (noms composés...).


1
artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024 7
23 août 2023 à 15:00

Salut blux,

J'ai intégré une boucle de lecture à ton script. Ça ne fonctionne pas tout à fait (mais c'est près). Il y a deux failles :


1) Si la ligne se termine par une (ou des lettres majuscules), le script ajoute le mot avec un trait d'union à la fin du bloc, dans le fichier, ce qui ne doit pas être.


Exemple : Pour la ligne suivante :


bien-ABN


on doit avoir :


bien-A
bien-B
bien-N


et non pas :


bien-A
bien-B
bien-N
bien-


2) Entre les lignes qui contiennent des mots différents, il y a une ligne vide, entre eux, dans le fichier :


Voici mon programme :

use strict;
use warnings;

# Ouverture des fichiers d'entrée et de sortie

open(FICHIER_ENTREE, "entree.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_SORTIE, ">sortie.txt") or die "Le fichier ne s'ouvre pas: $!";

# Début du traitement

while(<FICHIER_ENTREE>)
{
my $LIGNE_LUE = "$_";
my @tmp = split(/-/,$LIGNE_LUE);
my $mot = $tmp[0];
my @attribs = split(//,$tmp[1]);

foreach my $attrib (@attribs)
{
 print FICHIER_SORTIE $mot."-".$attrib."\n";
}
}

Voici mes deux fichiers :


entree.txt

accrocher-V
aéronef-
agile-A
bien-ABN
éducatrice-AN


sortie.txt

accrocher-V
accrocher-


aéronef- 


agile-A
agile-
           


bien-A
bien-B
bien-N
bien-
             


éducatrice-A
éducatrice-N
éducatrice-



Merci

0
blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024 3 289 > artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024
Modifié le 24 août 2023 à 11:18

Non, pour moi, si on rajoute le mot avec un simple tiret, c'est qu'il y a un caractère supplémentaire (invisible) derrière les attributs.

Peux-tu ajouter la ligne suivante après le split de $tmp[1]:

print scalar @attribs;

Elle va donner la taille du tableau (le nb d'attributs) et c'est sûrement ici que tu verras qu'il y a un loup (3 au lieu de 2, par exemple).

Dont une explication pourrait être la suivante :

Tu es sous windows ou linux ? Il est possible que la fin de ligne soit considérée comme un caractère et donc traitée en tant qu'attribut.

Tu pourrais dans ce cas, faire un :

chomp($tmp[1]);

avant le split (ou le faire sur $LIGNE_LUE avant son split).

C'est sûrement ce qui fait que tu as une ligne entre chaque mot. En fait, on affiche le mot et son attribut mais comme l'attribut est très vraisemblablement un saut de ligne, ça donne ce résultat de ligne blanche en trop.

Tu peux aussi rajouter le module Data::Dumper en tête de programme avec :

use Data::Dumper;

et voir ce que contiennent les différentes variables :

print Dumper @attribs;


Je pense que mon programme est correct depuis le début ;-))
 

En simplifiant, ça pourrait donner ça :
 

while(<FICHIER_ENTREE>)
{
    my $LIGNE_LUE = "$_";
    chomp($LIGNE_LUE);
    my ($mot,$attribs) = split(/-/,$LIGNE_LUE);
    my @attribs = split(//,$attribs);
    foreach my $attrib (@attribs)
    {
         print FICHIER_SORTIE $mot."-".$attrib."\n";
    }
}
1
artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024 7 > blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024
24 août 2023 à 16:36

Salut,

Je travaille sous Linux Mint.

Mon fichier d'entrée est :

accrocher-V
aéronef-
agile-A
bien-ABN
éducatrice-AN

1) J'ai fait ta ligne de commande "print scalar @attribs;" après le split de $tmp[1]:

while(<FICHIER_ENTREE>)
{
my $LIGNE_LUE = "$_";
my @tmp = split(/-/,$LIGNE_LUE);
my $mot = $tmp[0];
my @attribs = split(//,$tmp[1]);

print scalar @attribs;
system ("sleep 2");

foreach my $attrib (@attribs)
{
 print FICHIER_SORTIE $mot."-".$attrib."\n";
}
}

Résultat : 21243
Donc, c'est correct : 2 (accrocher-V) // 1 (aéronef-) // 2 (agile-A) ...

2) Ta suggestion d'utiliser la commande chomp m'a rappelé que quelqu'un m'avait déjà conseillé d'utiliser cette commande, il y a longtemps, dans un autre script. J'aurais dû y penser.
J'ai ajouté ton autre ligne "chomp($tmp[1]);" avant le split, mais ça ne fonctionne pas correctement: si la ligne se termine par un trait d'union, le programme ne copie pas la ligne dans le fichier ...

while(<FICHIER_ENTREE>)
{
my $LIGNE_LUE = "$_";
my @tmp = split(/-/,$LIGNE_LUE);

my $mot = $tmp[0];
chomp($tmp[1]);
my @attribs = split(//,$tmp[1]);

foreach my $attrib (@attribs)
{
 print FICHIER_SORTIE $mot."-".$attrib."\n";
}

Résultat :

accrocher-V
agile-A
bien-A
bien-B
bien-N
éducatrice-A
éducatrice-N


3) Si j'utilise la ligne de commande "print Dumper @attribs;" ça donne

$VAR1 = 'V';
$VAR1 = 'A';
$VAR1 = 'A';
$VAR2 = 'B';
$VAR3 = 'N';
$VAR1 = 'A';
$VAR2 = 'N';

Donc, pour la ligne "aéronef-", la variable $VAR1 n'a pas de contenu.

4) Ton programme fonctionne correctement si j'ajoute un espacement après le tiret, soit "aéronef- ". C'est ce que je vais faire je crois (ou simplement ajouter la lettre N pour les noms).
5) Dans les mots composés, je vais utiliser l'astérisque au lieu du trait d'union (arc*en*ciel) sinon, ça ne fonctionne pas. C'est simple ensuite de les remplacer, par la suite, par un tiret.

Je ne croyais pas que c’était aussi compliqué.

Merci

0
blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024 3 289 > artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024
Modifié le 24 août 2023 à 16:55

Donc, il faut faire un test si scalar @attribs est égal à zéro pour ne pas passer dans la boucle d'affichage des attributs et n'afficher que le nom (en supposant que tu as fait le chomp).

if ((scalar @attribs) != 0)
{
    foreach my $attrib (@attribs)
    {
     print FICHIER_SORTIE $mot."-".$attrib."\n";
    }
}
else
{
    print FICHIER_SORTIE $mot."-\n";
}

Ca devrait résoudre pas mal de pb...

Et pourquoi ne mets-tu pas l'astérisque comme séparateur entre un mot et ses attributs ?

Dans ce cas, il faudra changer :

my @tmp = split(/-/,$LIGNE_LUE);

par

my @tmp = split(/\*/,$LIGNE_LUE);

En Perl, ce qui sert de séparateur à la commande split est une expression régulière et comme l'astérisque a un rôle dans les regex, on la déspécialise avec un backslash (\).

0
blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024 3 289 > artagon7 Messages postés 415 Date d'inscription dimanche 6 novembre 2005 Statut Membre Dernière intervention 11 février 2024
Modifié le 25 août 2023 à 09:29

Ligne 45, mettre @attribs au lieu de $attribs, désolé. J'ai écrit sans tester...

1
blux Messages postés 26013 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 26 avril 2024 3 289
22 août 2023 à 17:10

Sinon, quand tu passes une regex, $1, $2... $N sont réinitialisées.

Si tu veux les conserver, il faut les mettre ailleurs :

$TOTO = $1;
$TATA = $2;

0