简体   繁体   中英

Can I substitute multiple items in a single regular expression in VIM or Perl?

Let's say I have string "The quick brown fox jumps over the lazy dog" can I change this to "The slow brown fox jumps over the energetic dog" with one regular expression? Currently, I use two sets of regular expressions for this situation. (In this case, I use s/quick/slow/ followed by s/lazy/energetic/ .)

You can do this in vim using a Dictionary:

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

This will change the following text:

The quick brown fox ran quick ly next to the lazy brook.

into:

The slow brown fox ran slow ly next to the energetic brook.

To see how this works, see :help sub-replace-expression and :help Dictionary . In short,

  • \\= lets you substitute in the result of a vim expression.
  • {'quick':'slow', 'lazy':'energetic'} is a vim dictionary (like a hash in perl or ruby, or an object in javascript) that uses [] for lookups.
  • submatch(0) is the matched string

This can come in handy when refactoring code - say you want to exchange the variable names for foo , bar , and baz changing

  • foobar
  • barbaz
  • bazfoo

Using a sequence of %s/// commands would be tricky, unless you used temporary variable names - but you'd have to make sure those weren't hitting anything else. Instead, you can use a Dictionary to do it in one pass:

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

Which changes this code

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

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

into:

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

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

If you find yourself doing this a lot, you may want to add the following to your ~/.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>)

This lets you use the :Refactor {'frog':'duck', 'duck':'frog'} command, and is slightly less repetitive than creating the regex for the dict manually.

The second part of a substitution is a double quoted string, so any normal interpolation can occur. This means you can use the value of the capture to index into a hash:

#!/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";

You can concatenate vim substitutions:

The quick brown fox ran quickly next to the lazy brook.

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

The slow brown fox ran quickly next to the energetic brook.

The advantage here is that you have to type your substitutions just once

Rgds

You can do the following.

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

The trick is to use the parens to match the text between the two words. You can then reference this text in the substitution string by using \\1 . You can also use \\2 for the second matched paren expression and so on. This allows you to replace multiple words without disturbing the text inbetween.

In perl:

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

In vim:

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

Chas's answer is good, the only other thing I'd mention is that if you're doing word swaps you probably want to be matching on

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

to avoid matching substrings. If you doing a lot of word swapping, you might start to find regexps a bit limiting and want to do something like:

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

There's a neat way to do it in Ruby using gsub with a block:

s = "The quick brown fox jumps over the lazy dog"
subs = {'quick' => 'slow', 'lazy' => 'industrious'}
s.gsub(/quick|lazy/) { |match| subs[match] }
# => "The slow brown fox jumps over the industrious dog"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM