tags:

views:

2101

answers:

7

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/.)

thanks.

+7  A: 

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.

JaredPar
What if there are many occurrences of quick and lazy that are interleaved?
allyourcode
@allyourcode, then it will replace the outermost pair. This is only slighty different than Tg's example which would replace the first occurance.
JaredPar
+13  A: 

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";
Chas. Owens
You forgot `e` modifier.
Hynek -Pichi- Vychodil
What does that do? evaluate the replacement? Is there something like this in other programming languages?
allyourcode
It doesn't need /e becuase it is a simple interpolation, try the code before making claims.
Chas. Owens
Yes, you are right.
Hynek -Pichi- Vychodil
this approach will have problems if you'll ever want to have 2 replace strings that one of them is prefix of another (for example: "dep"=>1, "depesz" => 2). to avoid the problem you should sort the keys of %replace by descreasing length of key.
depesz
+1  A: 

In perl:

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

In vim:

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

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"
allyourcode
A: 

Use this solution.

Hynek -Pichi- Vychodil
+9  A: 
rampion
A: 

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
Sharkey