我可以在VIM或Perl中的单个正则表达式中替换多个项目吗?

时间:2009-04-19 19:00:58

标签: regex perl vim

让我说我有一个字符串“快速的棕色狐狸跳过懒狗”我可以用一个正则表达式将其更改为“慢棕狐跳过精力充沛的狗”吗?目前,我为这种情况使用了两组正则表达式。 (在这种情况下,我使用s/quick/slow/后跟s/lazy/energetic/。)

7 个答案:

答案 0 :(得分:49)

您可以使用词典在vim中执行此操作:

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

这将更改以下文字:

  

快速棕色狐狸在懒惰小溪旁边快速

成:

  

棕色狐狸在精力充沛的小溪旁边

要了解其工作原理,请参阅:help sub-replace-expression:help Dictionary。简而言之,

  • \=允许您替换vim表达式的结果。
  • {'quick':'slow', 'lazy':'energetic'}是一个vim字典(如perl或ruby中的哈希,或javascript中的对象),它使用[]进行查找。
  • submatch(0)是匹配的字符串

在重构代码时,这可以派上用场 - 比如你要交换foobarbaz更改

的变量名称
  • foobar
  • barbaz
  • bazfoo

使用一系列%s///命令会很棘手,除非您使用临时变量名称 - 但是您必须确保这些命令没有发生任何其他事情。相反,您可以使用字典一次完成:

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

这改变了这段代码

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

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

成:

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

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

如果您发现自己经常这样做,可能需要将以下内容添加到~/.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>)

这允许您使用:Refactor {'frog':'duck', 'duck':'frog'}命令,稍微有点 比手动创建dict的正则表达式重复性更小。

答案 1 :(得分:32)

替换的第二部分是双引号字符串,因此可以进行任何正常插值。这意味着您可以使用捕获的值来索引哈希:

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

答案 2 :(得分:14)

您可以连接vim替换:

快速的棕色狐狸在懒惰的小溪旁边跑得很快。

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

缓慢的棕色狐狸在精力充沛的小溪旁快速奔跑。

这里的优点是您只需输入一次替换

RGDS

答案 3 :(得分:8)

您可以执行以下操作。

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

诀窍是使用parens来匹配两个单词之间的文本。然后,您可以使用\1在替换字符串中引用此文本。您还可以使用\2作为第二个匹配的paren表达式,依此类推。这允许您替换多个单词而不会干扰其间的文本。

答案 4 :(得分:2)

在perl:

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

在vim中:

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

答案 5 :(得分:0)

Chas的答案很好,我唯一提到的另一件事是,如果你正在进行单词交换,你可能想要匹配

\ B(FOO |酒吧|巴兹| qux)\ B'/ P>

避免匹配子串。如果您正在进行单词交换 lot ,您可能会开始发现regexps有点限制,并希望执行以下操作:

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

答案 6 :(得分:-2)

使用带有块的gsub在Ruby中有一种巧妙的方法:

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"