Могу ли я заменить несколько элементов в одном регулярном выражении в VIM или Perl? - PullRequest
28 голосов
/ 19 апреля 2009

Допустим, у меня есть строка "Быстрая коричневая лиса перепрыгивает через ленивую собаку", могу ли я изменить это на "Медленная коричневая лиса перепрыгивает через энергичную собаку" с одним регулярным выражением? В настоящее время я использую два набора регулярных выражений для этой ситуации. (В этом случае я использую s/quick/slow/, а затем s/lazy/energetic/.)

Ответы [ 7 ]

50 голосов
/ 20 апреля 2009

Вы можете сделать это в 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) соответствует строке

Это может пригодиться при рефакторинге кода - скажем, вы хотите обменять имена переменных на foo, bar и baz, меняющие

  • foo & rarr; bar
  • bar & rarr; baz
  • baz & rarr; foo

Использовать последовательность команд %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 вручную.

32 голосов
/ 19 апреля 2009

Вторая часть подстановки - строка в двойных кавычках, поэтому может произойти любая нормальная интерполяция. Это означает, что вы можете использовать значение захвата для индексации в хэш:

#!/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";
14 голосов
/ 03 мая 2012

Вы можете объединить замены vim:

Быстрая коричневая лиса быстро побежала рядом с ленивым ручьем.

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

Медленный бурый лис быстро побежал рядом с энергичным ручьем.

Преимущество здесь в том, что вы должны вводить замену только один раз

1010 * Rgds *

8 голосов
/ 19 апреля 2009

Вы можете сделать следующее.

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

Хитрость заключается в том, чтобы использовать парены, чтобы сопоставить текст между двумя словами. Затем вы можете ссылаться на этот текст в строке подстановки, используя \1. Вы также можете использовать \2 для второго совпадения выражения и так далее. Это позволяет заменить несколько слов, не нарушая промежуточный текст.

2 голосов
/ 19 апреля 2009

В perl:

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

В vim:

s/quick\(.*\)lazy/slow\1energetic/;
0 голосов
/ 20 апреля 2009

Ответ Часа хорош, единственное, что я хотел бы упомянуть, это то, что если вы делаете обмен словами, вы, вероятно, захотите соответствовать на

\ б (Foo | Бар | Баз | QUX) \ б

, чтобы избежать совпадения подстрок. Если вы делаете много обмена словами, вы можете начать находить регулярные выражения немного ограничивающими и захотите сделать что-то вроде:

join '', map { exists $subst{$_} ? $subst{$_} : $_ } split /\b/, $string
0 голосов
/ 19 апреля 2009

Есть хороший способ сделать это в Ruby, используя gsub с блоком:

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...