Can I substitute multiple items in a single regular expression in VIM or Perl?
假设我有一条线"快速的棕色狐狸跳过懒惰的狗",我能用一个正则表达式把它改成"缓慢的棕色狐狸跳过充满活力的狗"吗?目前,对于这种情况,我使用两组正则表达式。(在本例中,我使用s/quick/slow/,然后使用s/lazy/energetic/。
您可以使用字典在VIM中执行此操作:
1
| :%s/quick\ |lazy /\ ={'quick':'slow','lazy':'energetic'}[submatch (0)]/g |
。
这将更改以下文本:
The quick brown fox ran quickly next to the lazy brook.
号
进入:
The slow brown fox ran slowly next to the energetic brook.
号
要了解这是如何工作的,请参阅:help sub-replace-expression和:help Dictionary。简而言之,
- \=允许您替换vim表达式的结果。
- {'quick':'slow', 'lazy':'energetic'}是一个VIM字典(像Perl或Ruby中的散列,或javascript中的对象),使用[]进行查找。
- submatch(0)是匹配的字符串
这在重构代码时非常有用,比如您想将变量名替换为foo、bar和baz更改
- foo→bar。
- bar→baz。
- baz→foo。
除非使用临时变量名,否则使用一系列%s///命令是很困难的,但必须确保这些命令不会影响其他任何内容。相反,您可以使用字典一次完成:
1
| :%s/\ <\ %(foo\ |bar\ |baz\ )\ >/\ ={'foo':'bar','bar':'baz','baz':'foo'}[submatch (0)]/g |
。
它改变了这个代码
1 2 3 4 5 6
| int foo = 0;
float bar = pow (2.0, (float ) foo );
char baz [256] = {};
sprintf(baz ,"2^%d = %f
", foo , bar ); |
进入:
1 2 3 4 5 6
| int bar = 0;
float baz = pow (2.0, (float ) bar );
char foo [256] = {};
sprintf(foo ,"2^%d = %f
", bar , baz ); |
。
如果你发现自己经常这样做,你可能想在你的~/.vimrc中增加以下内容:
1 2 3 4 5 6 7
| " 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 () |
这允许您使用:Refactor {'frog':'duck', 'duck':'frog'}命令,并且稍微比手动为dict创建regex的重复性更低。
替换的第二部分是双引号字符串,因此可以进行任何正常的插值。这意味着您可以使用捕获的值对哈希进行索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #!/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
"; |
号
- 您忘记了e修饰符。
- 那有什么用?评估替代品?在其他编程语言中是否存在类似的情况?
- 它不需要/e,因为它是一个简单的内插,所以在声明之前尝试代码。
- 是的,你是对的。
- 如果您希望有两个替换字符串,其中一个是另一个的前缀(例如:"dep"=>1,"depesz"=>2),则此方法会有问题。为了避免这个问题,您应该通过减小键的长度来对%replace的键进行排序。
您可以连接VIM替换:
那只敏捷的棕色狐狸在那条懒惰的小溪边跑得很快。
1
| :s/quick /slow /|s/lazy /energetic / |
号
那只慢腾腾的棕色狐狸很快地跑到了那条充满活力的小溪边。
这里的优点是你只需要输入一次替代品
RGDS
- 不是问题所要求的单个正则表达式,而是+1以获得问题VIM部分imho的最佳解决方案。谢谢!
您可以执行以下操作。
1
| :%s/quick\ (.*\ )lazy /slow\1energetic |
诀窍是使用parens来匹配两个单词之间的文本。然后可以使用\1在替换字符串中引用此文本。您还可以使用\2来表示第二个匹配的paren表达式等。这允许您替换多个单词,而不会干扰中间的文本。
- 如果有许多快速和懒惰的事件是交错的呢?
- @所有代码,然后它将替换最外面的一对。这与TG的例子稍有不同,后者将取代第一次发生。
在Perl中:
1
| s/quick (.*)lazy /slow ${1}energetic /; |
在VIM中:
1
| s/quick\ (.*\ )lazy /slow\1energetic /; |
。
查斯的回答很好,我唯一要提到的是,如果你在做单词交换,你可能想匹配
b(foo_bar_baz_qux)b
以避免匹配子字符串。如果你做了大量的换词,你可能会开始发现regexps有点限制,并想做如下的事情:
。
在Ruby中,有一种简洁的方法可以使用带有块的gsub来完成这项工作:
1 2 3 4
| 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" |