Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

=c3‰changer des cha=c3=aenes

4 réponses
Avatar
Olivier Miakinen
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,
--
Olivier Miakinen

4 réponses

Avatar
Nicolas George
Olivier Miakinen , dans le message <ql350h$2il9$,
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.
Avatar
Olivier Miakinen
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 barn bar bonjour foo bar titin";
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 !
--
Olivier Miakinen
Avatar
Nicolas George
Olivier Miakinen , dans le message <ql3av3$2k1j$,
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.
Avatar
Olivier Miakinen
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.
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.

Oui, ok. Ça peut être une piste pour automatiser l'histoire des
assertions. Merci pour tout, Nicolas.
--
Olivier Miakinen