Замена регулярного выражения в Perl на границе слова.Обнаружение "/" как границы слова - PullRequest
0 голосов
/ 12 августа 2010

У меня странная проблема с регулярным выражением .... У меня есть документ, где я делаю замену ... в качестве примера я хочу заменить "DEXX" с "DEXX / AREX" а потом при следующей замене заменить ... "AREX" с "AREX / CUBE"

DEXX и AREX хранятся в хэше примерно так ... "DEXX" => "AREX", "AREX" => "CUBE"

регулярное выражение у меня есть это .....

foreach (keys %hashstore){
    $doc=~s!\b($_)\b!$1/$hashstore{$_}!ig;
}

Что происходит, так это то, что «DEXX» заменяется на «DEXX / AREX», но когда встречается «DEXX / AREX», регулярное выражение заменяет «DEXX / AREX» на «DEXX / AREX / CUBE», когда оно заменять «AREX», когда оно находит его как отдельное слово, а не как часть другой комбинации, такой как «DEXX / AREX»

Кажется, он определяет "/" как границу слова. Кто-нибудь сталкивался с этим или знал об исправлении вокруг этого? Большое спасибо! Amy

Ответы [ 5 ]

5 голосов
/ 12 августа 2010

Но / - это граница слова. От perldoc perlreref :

\b Соответствует границе слова (между \w и \W).

В свете вашего комментария ниже, вам следует избегать цикла:

#!/usr/bin/perl

use strict; use warnings;
use Regex::PreSuf;

my %lookup = (
    "DEXX" => "AREX",
    "AREX" => "CUBE",
);

my $doc = 'DEXX AREX AREX DEXX AREX DEXX DEXX DEXX AREX';
my $re = presuf keys %lookup;

$doc =~ s{($re)}{$1/$lookup{$1}}g;

print $doc, "\n";

Выход:

DEXX/AREX AREX/CUBE AREX/CUBE DEXX/AREX AREX/CUBE DEXX/AREX DEXX/AREX DEXX/AREX
AREX/CUBE

Конечно, вам не нужно использовать Regex :: PreSuf , если у вас есть только два ключа:

s{(AREX|DEXX)}{$1/$lookup{$1}}g;

тоже подойдет. Но для более длинного списка ключей я нахожу Regex :: PreSuf очень удобным.

Обновление: Конечно, если в любом случае в тексте могут встречаться клавиши, вы можете использовать uc для преобразования при поиске замены:

Итак, либо

$doc =~ s{($re)}{join '/', uc($1), $lookup{uc $1}}eig;

или

$doc =~ s{($re)}{join '/', $1, $lookup{uc $1}}eig;

в зависимости от того, что вам нужно.

Кроме того, ysth указывает в комментариях: «С 5.10 и более поздними Regex :: PreSuf генерирует более регулярное выражение, чем наивное чередование в большинстве случаев» Итак,

my $re = join '|', map quotemeta, sort { length($b) <=> length($a) } keys %lookup; 

может быть лучше. sort необходим, если некоторые ключи могут быть начальными подстроками других ключей.

2 голосов
/ 12 августа 2010

\ b эквивалентно (хотя и более эффективно, чем) (?:(?<!\w)(?=\w)|(?<=\w)(?!\w)). Если вы хотите, чтобы набор символов слова отличался от набора по умолчанию, просто используйте его, но с заменой \ w на соответствующий класс символов.

2 голосов
/ 12 августа 2010

Граница слова - это всякий раз, когда происходит переход между \w и \W, то есть [a-zA-Z0-9_] и [^a-zA-Z0-9_], если вы имеете дело с ASCII.

Вы должны быть в состояниичтобы обойти эту проблему с помощью негативного взгляда:

foreach (keys %hashstore){
    $doc=~s!(?<!/)\b($_)\b!$1/$hashstore{$_}!ig;
}
1 голос
/ 12 августа 2010

Прежде всего, я в долгу перед Синаном (который не относится к Perl на SO? Я знаю, что я скрывался долгое время ....) и да.Благодаря этим двум я лучше понимаю регулярные выражения.Однако мое решение заключалось в следующем ...

my $pat = join '|', keys(%hashstore);
$doc =~ s!\b($pat)\b!$1/$hashstore{uc($1)}!ig;

Проблема, с которой я столкнулся, заключалась в замене замен!Обычно я действительно пытаюсь уладить эти вещи, но это был такой сжатый срок, и Синан, да, вы оба сильно качаетесь!Amy

0 голосов
/ 18 ноября 2010

Границы с точки зрения \b часто не совсем то, что вы хотите, особенно с учетом того, что английские слова могут содержать апострофы и тире, и что они ведут себя совершенно иначе, чем буквы, когда рядом с ними ставится \b.См. этот ответ для более подробного объяснения этой проблемы, и , что с этим делать.

...