web-dev-qa-db-fra.com

Puis-je substituer plusieurs éléments dans une seule expression régulière dans VIM ou Perl?

Disons que j'ai la chaîne "Le renard brun rapide saute par-dessus le chien paresseux" puis-je changer ceci en "Le renard brun lent saute par-dessus le chien énergique" avec une expression régulière? Actuellement, j'utilise deux ensembles d'expressions régulières pour cette situation. (Dans ce cas, j'utilise s/quick/slow/ suivi de s/lazy/energetic/.)

28
Tg.

La deuxième partie d'une substitution est une chaîne à double guillemet, de sorte que toute interpolation normale peut avoir lieu. Cela signifie que vous pouvez utiliser la valeur de la capture pour indexer dans un hachage:

#!/usr/bin/Perl

use strict;
use warnings;


my %replace = (
    quick => "slow",
    lazy  => "energetic",
);

my $regex = join "|", keys %replace;
$regex = qr/$regex/;

my $s = "The quick brown fox jumps over the lazy dog";

$s =~ s/($regex)/$replace{$1}/g;

print "$s\n";
32
Chas. Owens

Vous pouvez le faire dans vim en utilisant un dictionnaire:

:%s/quick\|lazy/\={'quick':'slow','lazy':'energetic'}[submatch(0)]/g

Cela va changer le texte suivant:

Le rapide renard brun a couru rapide ly à côté du paresseux ruisseau.

dans:

Le le - lent renard brun a couru lentement tout près du energetic ruisseau.

Pour voir comment cela fonctionne, voir :help sub-replace-expression et :help Dictionary. En bref,

  • \= vous permet de substituer le résultat d'une expression vim. 
  • {'quick':'slow', 'lazy':'energetic'} est un dictionnaire vim (comme un hachage en Perl ou Ruby, ou un objet en javascript) qui utilise [] pour les recherches. 
  • submatch(0) est la chaîne correspondante 

Cela peut s'avérer utile lors du refactoring de code - par exemple, vous voulez échanger les noms de variable pour foo, bar et baz en changeant 

  • foobar
  • barbaz
  • bazfoo

L'utilisation d'une séquence de commandes %s/// serait délicate, à moins d'utiliser des noms de variables temporaires, mais vous devez vous assurer que celles-ci ne touchent rien d'autre. Au lieu de cela, vous pouvez utiliser un dictionnaire pour le faire en un seul passage:

:%s/\<\%(foo\|bar\|baz\)\>/\={'foo':'bar','bar':'baz','baz':'foo'}[submatch(0)]/g

Ce qui change ce code

int foo = 0;
float bar = pow(2.0, (float) foo);
char baz[256] = {};

sprintf(baz,"2^%d = %f\n", foo, bar);

dans:

int bar = 0;
float baz = pow(2.0, (float) bar);
char foo[256] = {};

sprintf(foo,"2^%d = %f\n", bar, baz);

Si vous vous trouvez souvent en train de faire cela, vous voudrez peut-être ajouter ce qui suit à votre ~/.vimrc:

" Refactor the given lines using a dictionary
" replacing all occurences of each key in the dictionary with its value
function! Refactor(dict) range
  execute a:firstline . ',' . a:lastline .  's/\C\<\%(' . join(keys(a:dict),'\|'). '\)\>/\='.string(a:dict).'[submatch(0)]/ge'
endfunction

command! -range=% -nargs=1 Refactor :<line1>,<line2>call Refactor(<args>)

Cela vous permet d'utiliser la commande :Refactor {'frog':'duck', 'duck':'frog'} et est légèrement moins moins répétitif que la création manuelle de l'expression rationnelle pour le dict.

49
rampion

Vous pouvez concaténer les substitutions de vim:

Le renard brun rapide a couru rapidement à côté du ruisseau paresseux.

:s/quick/slow/|s/lazy/energetic/

Le lent renard brun a couru rapidement à côté du ruisseau énergétique.

L’avantage ici est que vous devez taper vos substitutions une seule fois.

Rgds

14
joe_zeroh

Vous pouvez faire ce qui suit.

:%s/quick\(.*\)lazy/slow\1energetic

L'astuce consiste à utiliser les parenthèses pour faire correspondre le texte entre les deux mots. Vous pouvez ensuite référencer ce texte dans la chaîne de substitution en utilisant \1. Vous pouvez également utiliser \2 pour la deuxième expression paren correspondante, etc. Cela vous permet de remplacer plusieurs mots sans perturber le texte entre les deux. 

8
JaredPar

En Perl:

s/quick(.*)lazy/slow${1}energetic/;

En vim:

s/quick\(.*\)lazy/slow\1energetic/;
2
user80168

La réponse de Chas est bonne, la seule autre chose que je voudrais mentionner est que si vous effectuez des échanges de mots, vous voulez probablement faire correspondre 

\ b (foo | bar | baz | qux)\b

pour éviter de faire correspondre les sous-chaînes. Si vous faites un lot de permutation de Word, vous pourriez commencer à trouver les expressions rationnelles un peu limitantes et à vouloir faire quelque chose comme:

join '', map { exists $subst{$_} ? $subst{$_} : $_ } split /\b/, $string
0
NickZoic