perl: заменить шаблон шаблоном другого размера

вот моя строка A B C D, и я хочу, например, заменить A на 123 и C на 456. Однако это не работает.

$string=~ s/A|B|C|D/123|B|456|D/;

Я хочу это 123 B 456 D, но я получаю это 123|B|456|D B C D

Наверное, потому что количество символов в двух моих шаблонах разное.

Есть ли способ заменить шаблоны разного размера с помощью другого фрагмента кода? Большое спасибо.


person chris202    schedule 12.06.2015    source источник
comment
Это должно работать. каков ваш вывод?   -  person Jens    schedule 13.06.2015
comment
Относительные размеры шаблона и замещающей строки не имеют значения. Ваш пример кода отлично работает со мной. Можете ли вы показать полный код?   -  person collapsar    schedule 13.06.2015


Ответы (4)


Что-то вроде этого с использованием eval (непроверено).

$string=~ s/(A)|C/ length($1) ? '123': '456'/eg;  

Использование флага eval в форме s/// означает оценку стороны replacement
как строки кода, которая возвращает значение.

В этом случае он выполняет троичное условие в коде замены.

Это похоже на обратный вызов встроенного регулярного выражения.
Это гораздо сложнее, поскольку может быть похоже на s///eeg, поэтому
лучше обратиться к документации.

Помните, что eval действительно зло, написано с ошибкой!!

person Community    schedule 12.06.2015
comment
Это работает, спасибо! Не могли бы вы немного объяснить формулу? - person chris202; 13.06.2015
comment
Обратите внимание, что, несмотря на несколько вводящую в заблуждение аббревиатуру, /e на самом деле не имеет ничего общего с eval. Это просто приводит к тому, что правая часть подстановки анализируется (во время компиляции) как код Perl, а не как строка (в двойных кавычках). Чтобы еще больше запутать ситуацию, /ee действительно применяется eval к результат подстановки и разделяет его зло (неэффективность, отсутствие проверки ошибок во время компиляции, очень высокий риск дыр в безопасности при применении к пользовательскому вводу). - person Ilmari Karonen; 13.06.2015

Вы получаете то, что я ожидал получить. Ваше регулярное выражение ищет одно вхождение 'A', 'B', 'C' или 'D' и заменяет его буквальной строкой '123|B|456|D'. Следовательно, 'A B C D' -> '123|B|456|D B C D'

Таким образом, он находит первое вхождение, 'A', и заменяет его указанной вами строкой. альтернация соответствует различным строкам, но символы вертикальной черты ничего не значат в слоте замены.

Что вам нужно сделать, так это создать сопоставление между входом и выходом, например:

my %map = ( A => '123', C => '456' );

Тогда вам нужно использовать его в заменах. Дадим вам поисковое выражение:

my $search = join( '|', keys %map );

Теперь давайте напишем замену (я предпочитаю фигурные скобки, когда кодирую замены, в которых есть код:

$string =~ s{($search)}{ $map{$1} }g;

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

Выход '123 B 456 D'

person Axeman    schedule 12.06.2015
comment
Нет необходимости в модификаторе /e, когда замена может быть выражена в виде простой интерполированной строки. $string =~ s/($search)/$map{$1}/g в порядке - person Borodin; 13.06.2015

Проще всего это сделать с помощью двух замен:

$string =~ s/A/123/g;
$string =~ s/B/456/g;

или даже (используя встроенный цикл for в качестве сокращения для применения нескольких замен к одной строке):

s/A/123/g, s/B/456/g for $string;

Конечно, для более сложных шаблонов это может не дать таких же результатов, как выполнение обеих подстановок за один проход; в частности, это может произойти, если шаблоны могут перекрываться (как в A = YZ, B = XY) или если шаблон B может соответствовать строке, заменяемой шаблоном A.

Если вы хотите сделать это за один проход, наиболее общим решением является использование /e модификатор, который интерпретирует замену как код Perl, например:

$string =~ s/(A|B)/ $1 eq 'A' ? '123' : '456' /eg;

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

$string =~ s{(A|B)}{
    my $foo = "";
    $foo = '123' if $1 eq 'A';
    $foo = '456' if $1 eq 'B';
    $foo;  # <-- this is what gets substituted for the pattern
}eg;

Если ваши шаблоны представляют собой постоянные строки (как в простых примерах выше), еще более эффективным решением будет использование хэша поиска, например:

my %map = ('A' => '123', 'B' => '456');
$string =~ s/(A|B)/$map{$1}/g;

С этим методом вам даже не нужен модификатор /e (хотя для этого конкретного примера его добавление не имеет значения). Преимущество использования /e заключается в том, что он позволяет реализовать более сложные правила выбора замены, чем позволяет простой поиск по хешу.

person Ilmari Karonen    schedule 12.06.2015

Строка = А Б В Г

эхо $String | perl - pi - e 's/A/123/' && perl - pi - e 's/C/456/'

person Krupa Kiran    schedule 15.01.2021