cerhu > comp.lang.* > comp.lang.perl

Olivier Miakinen (08/09/2019, 16h59)
Bonjour,

Je voudrais écrire un script qui échange des chaînes de caractères
dans un fichier texte.

Par exemple, je voudrais que tous les « foo » deviennent des « bar »
tandis que tous les « bar » deviennent des « foo », que tous les
« toto » deviennent des « titi » tandis que tous les « titi »
deviennent des « toto », etc. pour une quinzaine de mots.

Ainsi, le texte :
salut foo titi foo bar
bar bonjour foo bar titi
Deviendrait :
salut bar toto bar foo
foo bonjour bar foo toto

Vu que le texte ne contiendra pas certains caractères, je pourrais
le faire en plusieurs passes. Par exemple une première passe :
foo -> BAR
bar -> FOO
toto -> TITI
titi -> TOTO
Puis une deuxième passe :
BAR -> bar
FOO -> foo
TITI -> titi
TOTO -> toto

Mais existe-t-il une méthode plus efficace ? Idéalement, j'aimerais
bien écrire une seule fois mes deux listes de mots (ou ma liste de
paires de mots).

Cordialement,
Nicolas George (08/09/2019, 17h06)
Olivier Miakinen , dans le message <ql350h$2il9$1>,
a écrit :
> Mais existe-t-il une méthode plus efficace ? Idéalement, j'aimerais
> bien écrire une seule fois mes deux listes de mots (ou ma liste de
> paires de mots).


Si tes chaînes sont bien toutes constantes, tu peux accéder à une table
de hachage depuis l'expression de remplacement :

s/($re)/$map{$1}/g;

Tu peux facilement construire $re à partir de %map :

my $re = join("|", sort(keys(%map)));
$re = qr/$re/;

Je pense que tu es capable de faire le reste.

Si les chaînes ne sont pas constantes, tu peux faire pareil si tu as une
fonction qui implémente le remplacement individuel, en utilisant le flag
e à l'opérateur de remplacement.
Olivier Miakinen (08/09/2019, 18h41)
Le 08/09/2019 17:06, Nicolas George m'a répondu :
> Si tes chaînes sont bien toutes constantes,


Tu veux dire si ce ne sont pas des regexp ? Pour le moment elles
sont bien toutes constantes, mais je crains de devoir ajouter plus
tard des assertions à certaines d'entre elles.

Par exemple :
"abcd" => "truc"
"truc" => "abcd"
"abc(?!d)" => "machin"
"machin" => "abc"

> tu peux accéder à une table
> de hachage depuis l'expression de remplacement :
> s/($re)/$map{$1}/g;
> Tu peux facilement construire $re à partir de %map :
> my $re = join("|", sort(keys(%map)));
> $re = qr/$re/;
> Je pense que tu es capable de faire le reste.


J'ai eu du mal car je ne suis pas encore tout à fait au point en
perl, mais en m'aidant de la doc je suis arrivé à ceci :
----------------------------------------------------------------------
#!/usr/bin/perl
use strict;
use warnings;

my %map = ( "foo" => "bar", "toto" => "titi" );
%map = (%map, reverse %map);

my $re = join("|", keys(%map));
$re = qr/$re/;

my $phrase = " salut foo titi foo bar\n bar bonjour foo bar titi\n";
print "Avant :\n$phrase";
$phrase =~ s/($re)/$map{$1}/g;
print "Après :\n$phrase";
----------------------------------------------------------------------

Avec comme résultat :
----------------------------------------------------------------------
Avant :
salut foo titi foo bar
bar bonjour foo bar titi
Après :
salut bar toto bar foo
foo bonjour bar foo toto
----------------------------------------------------------------------

> Si les chaînes ne sont pas constantes, tu peux faire pareil si tu as une
> fonction qui implémente le remplacement individuel, en utilisant le flag
> e à l'opérateur de remplacement.


Ah oui, je crois que je vois ce que c'est. Je vais y réfléchir.

Encore merci !
Nicolas George (08/09/2019, 19h34)
Olivier Miakinen , dans le message <ql3av3$2k1j$1>,
a écrit :
> Tu veux dire si ce ne sont pas des regexp ? Pour le moment elles
> sont bien toutes constantes, mais je crains de devoir ajouter plus
> tard des assertions à certaines d'entre elles.


Dans ce cas, il va falloir ruser.

> my %map = ( "foo" => "bar", "toto" => "titi" );
> %map = (%map, reverse %map);


Je n'aurais pas utilisé reverse pour cet usage, mais ça marche.

> Ah oui, je crois que je vois ce que c'est. Je vais y réfléchir.


Ce n'est pas très compliqué:

sub replace_word($) {
my ($in) = $_;
return ...;
}

$text =~ s/($re)/replace_word($1)/ge;

Il ne reste qu'à écrire le code de replace_word.
Olivier Miakinen (08/09/2019, 19h51)
Le 08/09/2019 19:34, Nicolas George a écrit :
>> Tu veux dire si ce ne sont pas des regexp ? Pour le moment elles
>> sont bien toutes constantes, mais je crains de devoir ajouter plus
>> tard des assertions à certaines d'entre elles.

> Dans ce cas, il va falloir ruser.


Il faut mettre "abc(?!d)" dans la regexp mais "abc" => "machin" dans
le %map. Du coup il y a au moins une chaîne que j'écris deux fois
mais ce n'est pas insurmontable. Si je me rends compte plus tard que
j'ai besoin d'automatiser le truc, j'aviserai le moment venu.

>> my %map = ( "foo" => "bar", "toto" => "titi" );
>> %map = (%map, reverse %map);

> Je n'aurais pas utilisé reverse pour cet usage, mais ça marche.


Oui. J'ai essayé une syntaxe à base de map() mais je n'y suis pas
arrivé. Alors comme reverse() fonctionne j'ai trouvé que c'était
le plus simple.

> Ce n'est pas très compliqué:
> sub replace_word($) {
> my ($in) = $_;
> return ...;
> }
> $text =~ s/($re)/replace_word()/ge;
> Il ne reste qu'à écrire le code de replace_word.


Oui, ok. Ça peut être une piste pour automatiser l'histoire des
assertions. Merci pour tout, Nicolas.
Discussions similaires
fréquences des nouvelles chaines chaines

Où échanger son vin?

Où échanger des $ ?

A échanger.


Fuseau horaire GMT +2. Il est actuellement 19h58. | Privacy Policy