Подтвердить что ты не робот

Можно ли заменить несколько элементов в одном регулярном выражении в VIM или Perl?

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

4b9b3361

Ответ 1

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

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

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

  • 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 вручную.

Ответ 3

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

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

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

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

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

Rgds

Ответ 4

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

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

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

Ответ 5

В perl:

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

В vim:

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

Ответ 6

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

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

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

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

Ответ 7

Там аккуратный способ сделать это в 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"