Modification du nom d'un pointeur dans une boucle [Résolu]

Signaler
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019
-
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019
-
Bonjour,

J'ai écrit un programme qui cherche des rimes dans deux fichiers qui contiennent des mots qui commencent respectivement par "a" et "b". Lorsque le programme trouve un mot qui finit par une certaine terminaison, il l'ajoute dans le fichier correspondant au rime en question. Si un mot ne possède aucune terminaison qui est spécifée, le programme le met dans le fichier reste_(premiere lettre du mot).txt

#!/usr/bin/perl

open(FICHIER_BANQUE_DE_MOTS_A, "banque_a.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_BANQUE_DE_MOTS_B, "banque_b.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_RIMES_AIL, ">rimes_ail.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_RIMES_AN, ">rimes_an.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_RIMES_O, ">rimes_o.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_RESTES_A, ">restes_a.txt") or die "Le fichier ne s'ouvre pas: $!";
open(FICHIER_RESTES_B, ">restes_b.txt") or die "Le fichier ne s'ouvre pas: $!";

while(<FICHIER_BANQUE_DE_MOTS_A>)
{
$x = "$_";  

if ((/ail$/) or (/aille$/)) {print FICHIER_RIMES_AIL "$_"}
else 
{if ((/an$/) or (/anc$/) or (/ant$/) or (/ent$/)) {print FICHIER_RIMES_AN "$_"}
else 
{if ((/au$/) or (/aud$/) or (/aut$/) or (/eau$/) or (/o$/) or (/ot$/) or (/ôt$/)) 
{print FICHIER_RIMES_O "$_"}
else {print FICHIER_RESTES_A "$_"};
}
} 
} 

while(<FICHIER_BANQUE_DE_MOTS_B>)
{
$x = "$_";

if ((/ail$/) or (/aille$/)) {print FICHIER_RIMES_AIL "$_"}
else 
{if ((/an$/) or (/anc$/) or (/ant$/) or (/ent$/)) {print FICHIER_RIMES_AN "$_"}
else 
{if ((/au$/) or (/aud$/) or (/aut$/) or (/eau$/) or (/o$/) or (/ot$/) or (/ôt$/)) 
{print FICHIER_RIMES_O "$_"}
else {print FICHIER_RESTES_B "$_"};
}
} 
} 

close FICHIER_BANQUE_DE_MOTS_A;
close FICHIER_BANQUE_DE_MOTS_B;
close FICHIER_RIMES_O;
close FICHIER_RIMES_AIL;
close FICHIER_RIMES_AN;
close FICHIER_RESTES_A;
close FICHIER_RESTES_B;

Voici les deux fichiers de données que j'ai utilisés.

banque_a.txt
aloyau
an
artichaut
attirail
avant
avoir

banque_b.txt
barreau
bataille
bateau
blanc
boire
bordereau

Le programme fonctionne bien. Cependant, je veux l'appliquer à 26 fichiers (a à z).

Au lieu d'utiliser 26 boucles WHILE, je voudrais en avoir une seule dans laquelle je changerais le nom du pointeur. Ce serait beaucoup plus court. Mais surtout, si je dois apporter une modification dans la boucle, j'évite de le faire dans les 25 autres ...

Ainsi :
FICHIER_BANQUE_DE_MOTS_A deviendrait FICHIER_BANQUE_DE_MOTS_B puis deviendrait FICHIER_BANQUE_DE_MOTS_C ...

De même le pointeur FICHIER_RESTES_A deviendrait FICHIER_RESTES_B ... etc ...

Est-ce qu'il est possible de changer le nom d'un pointeur dans une boucle?

Je pourrais utiliser des chiffres au lieu de lettres. Je peux changer FICHIER_RESTES_A en FICHIER_RESTES_1 si ça simplifie les choses.

Merci

1 réponse

Messages postés
5380
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
3 juillet 2020
884
Salut artagon7,

As-tu besoin que les 26 fichiers soient ouverts en même temps (comme dans ton code où tu ouvres le fichier banque de mots en A et banque de mots en B), ou est-ce suffisant de les ouvrir un par un dans ta boucle (puisque dans ton code, tu les traites de toutes façon indépendamment) ?

1.

Si tu n'as pas besoin de les ouvrir tous en même temps, tu peux simplement :

- créer un tableau des noms de fichiers banque de mots
- itérer sur ce tableau des noms de fichiers (par exemple avec une boucle foreach)
- dans la boucle foreach ouvrir le fichier en question, en traiter le contenu et fermer le fichier

https://perlmaven.com/perl-arrays

2.

Si tu as vraiment besoin de les ouvrir tous en même temps (cela fait pas mal de ressources consommées sur un système) c'est faisable aussi si tu utilises des variables pour tes descripteurs de fichiers, au lieu d'identifiants globaux (je t'avais déjà indiqué que cette pratique est obsolète et peut poser des problèmes, outre le fait que tu n'utilises pas
use strict;
et
use warnings;
).

Tu peux les traiter dans une structure de données Perl comme un tableau ou un hash (le hash étant certainement plus approprié ici), et itérer dessus.

https://perlmaven.com/perl-hashes


Dal
Messages postés
5380
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
3 juillet 2020
884
En mode strict, les variables doivent être déclarées avant d'être utilisées.

Tu déclares une variable
$x
dans ton programme en faisant
my $x;
.

Tu peux aussi directement faire :
my $x = "$_"; 
pour déclarer la variable et immédiatement lui assigner quelque chose.
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019
6 >
Messages postés
5380
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
3 juillet 2020

Salut Dal,

Comme tu l'avais suggéré, j'ai déclaré la variable $x. Cependant, cela n'as pas marché du premier coup.

J'avais, quelques semaines auparavant, changé mon programme. En effet, j'avais enlevé la boucle while pour y mettre uniquement une boucle foreach.


Programme 1 - Ne fonctionne pas

#!/usr/bin/perl

use strict;
use warnings;

open(FICHIER_RIMES_AIL, ">rimes_ail.txt") or die "Le fichier de rimes ne s'ouvre pas: $!";

my @fichier = ("banque_a.txt", "banque_b.txt");
my $x;

foreach my $fichier (@fichier)
{
open(PNTR_FICHIER, $fichier) or die "Le fichier de mots ne s'ouvre pas: $!";
print $fichier, "\n";

$x = "$_";
if ((/ail$/) or (/aille$/)) {print FICHIER_RIMES_AIL "$_"}

close PNTR_FICHIER;
}

close FICHIER_RIMES_AIL;



Messages d'erreur

banque_a.txt
Use of uninitialized value $_ in string at forum-essai_1.pl line 16.
Use of uninitialized value $_ in pattern match (m//) at forum-essai_1.pl line 17.
Use of uninitialized value $_ in pattern match (m//) at forum-essai_1.pl line 17.
banque_b.txt
Use of uninitialized value $_ in string at forum-essai_1.pl line 16.
Use of uninitialized value $_ in pattern match (m//) at forum-essai_1.pl line 17.
Use of uninitialized value $_ in pattern match (m//) at forum-essai_1.pl line 17.



Je me suis aperçu qu'il y avait une erreur dans mon programme : je ne faisais le test

if ((/ail$/) or (/aille$/)) {print FICHIER_RIMES_AIL "$_"}


qu'une seule fois par fichier.

J'ai donc ajouté la boucle while dans la boucle foreach. Toutefois, je ne pensais pas que cela allait régler le problème. Surprise! Eh bien oui!


Programme 2 - Fonctionne

#!/usr/bin/perl

use strict;
use warnings;

open(FICHIER_RIMES_AIL, ">rimes_ail.txt") or die "Le fichier de rimes ne s'ouvre pas: $!";

my @fichier = ("banque_a.txt", "banque_b.txt");
my $x;

foreach my $fichier (@fichier)
{
open(PNTR_FICHIER, $fichier) or die "Le fichier de mots ne s'ouvre pas: $!";
print $fichier, "\n";

while(<PNTR_FICHIER>)
{
$x = "$_";
if ((/ail$/) or (/aille$/)) {print FICHIER_RIMES_AIL "$_"}
}
close PNTR_FICHIER;
}

close FICHIER_RIMES_AIL;



J'ai joué de chance. Je crois que c'est la boucle while qui initialise la valeur de $x. Pourtant, dans le premier programme, j'initialise aussi la variable $x. Je ne comprends pourquoi ça n'a pas fonctionné.

J'ai lu un peu sur la variable $_. C'est une variable globale qui est utilisée par défaut par les boucles for et while.

Merci beaucoup d'avoir résolu mon problème.
Messages postés
5380
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
3 juillet 2020
884 >
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019

Bravo !

Sur
$_
, c'est une des particularités de Perl, qui est souvent mal comprise par les personnes habituées à d'autres langages, et qui fait que Perl peut ressembler à de la magie noire pour elles.

$_
est une variable spéciale où Perl stocke le résultat de certaines opérations si le programme n'indique pas une autre variable où le résultat doit être stocké (c'est le cas, par exemple, pour le résultat de la lecture d'une ligne d'un fichier). De même, le contenu de cette variable peut être utilisé implicitement par Perl, lorsque certaines opérations sont réalisées sans qu'une variable ne soit fournie (c'est le cas, par exemple, lorsque tu tentes de matcher une regexp).

Dans ton premier programme, tu ne faisais qu'ouvrir chaque fichier, sans lire dedans, et c'est pourquoi la variable spéciale
$_ 
n'était initialisée avec rien et tu avais une erreur lorsque tu tentais dans ton
if ((/ail$/) or (/aille$/))
de matcher tes regexp (implicitement, puisque tu ne spécifies pas avec quelle variable les regexp doivent être comparées) avec
$_ 
.

Dans ton deuxième programme, la boucle
while(<PNTR_FICHIER>)
lit une ligne sur le handle du fichier ouvert et en met le contenu implicitement dans
$_ 
(car tu n'as indiqué aucune variable où mettre le contenu de la lecture). Dès lors,
$_ 
contient bien la ligne lue et peut être utilisé (implicitement) dans ton
if ((/ail$/) or (/aille$/))
pour vérifier si elle matche tes regexp.

Dans ton code, la variable
$x
n'est pas vraiment utilisée. Tu y mets juste le contenu de
$_ 
, mais tu n'en fais rien.

Pour l'utiliser réellement, tu pourrais explicitement mettre la ligne lue à chaque itération de
while(<PNTR_FICHIER>)
dans
$x
et effectuer tes comparaisons par rapport à tes regexp avec le contenu de
$x
.

Cela donnerait cela (code non testé) :

#!/usr/bin/perl

use strict;
use warnings;

open(FICHIER_RIMES_AIL, ">rimes_ail.txt") or die "Le fichier de rimes ne s'ouvre pas: $!";

my @fichier = ("banque_a.txt", "banque_b.txt");

foreach my $fichier (@fichier) {
    open(PNTR_FICHIER, $fichier) or die "Le fichier de mots ne s'ouvre pas: $!";
    print "$fichier\n";

    while (my $x = <PNTR_FICHIER>) {
        if (($x =~ /ail$/) or ($x =~ /aille$/)) {
            print FICHIER_RIMES_AIL "$x";
        }
    }                                                                                                    
    close PNTR_FICHIER;
}

close FICHIER_RIMES_AIL;

Tu as un petit article de Gabor Szabo sur
$_
: https://perlmaven.com/the-default-variable-of-perl

Il n'en est pas très fan, j'ai l'impression, mais perso, je trouve que pour des cas simples, cela rend le code plus lisible et, contrairement à Gabor, cela ne me gène pas d'écrire
print "$_"
plutôt que
print
, histoire de rappeler au lecteur que je travaille sur cette variable :-)
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019
6 >
Messages postés
5380
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
3 juillet 2020

Salut Dal,

J'ai essayé ton programme et il fonctionne. J'aurais été surpris du contraire...

J'aime mieux cette approche car c'est plus explicite.

Je vais aller lire l'article de Gabor Szabo sur la variable $_. J'ai bien apprécié tes explications concernant cette variable spéciale. J'en ai maintenant une bien meilleure compréhension.

Merci beaucoup,
Messages postés
283
Date d'inscription
dimanche 6 novembre 2005
Statut
Membre
Dernière intervention
21 décembre 2019
6
Salut Dal,

Le fait de déclarer la variable a fonctionné.
Je vais revenir d'ici une semaine (quand je serai moins occupé) pour clore le sujet car au début ça ne fonctionnait pas.

Merci.