Discussion:
Échanger des chaînes
(trop ancien pour répondre)
Olivier Miakinen
2019-09-08 14:59:29 UTC
Permalink
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
Nicolas George
2019-09-08 15:06:48 UTC
Permalink
Post by Olivier Miakinen
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
2019-09-08 16:41:06 UTC
Permalink
Post by Nicolas George
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"
Post by Nicolas George
tu peux accéder à une table
s/($re)/$map{$1}/g;
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
----------------------------------------------------------------------
Post by Nicolas George
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
Nicolas George
2019-09-08 17:34:11 UTC
Permalink
Post by Olivier Miakinen
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.
Post by Olivier Miakinen
my %map = ( "foo" => "bar", "toto" => "titi" );
%map = (%map, reverse %map);
Je n'aurais pas utilisé reverse pour cet usage, mais ça marche.
Post by Olivier Miakinen
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
2019-09-08 17:51:50 UTC
Permalink
Post by Nicolas George
Post by Olivier Miakinen
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.
Post by Nicolas George
Post by Olivier Miakinen
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.
Post by Nicolas George
Post by Olivier Miakinen
Ah oui, je crois que je vois ce que c'est. Je vais y réfléchir.
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
Loading...