2016-07-16 7 views
1

У меня есть регулярное выражение на языке Perl, который преобразует переносы пространств, например: -Perl Regex Удалить дефис, но игнорировать конкретные переносимые слова

$string =~ s/-/ /g; 

Мне нужно изменить это, чтобы игнорировать конкретный дефис фразы, а не заменять дефис, например, в строке, как это:

"use-either-dvi-d-or-dvi-i" 

Я хотел бы НЕ заменить дефис в дви-d и дви-я так гласит:

"use either dvi-d or dvi-i" 

Я пробовал различные отрицательный смотреть вперед матчи, но с треском провалились ,

+1

Что вы пробовали? Являются ли 'dvi-i' и' dvi-d' единственными фразами, которые вы хотите исключить? Или есть «белый список» из них? –

+0

На самом деле белый список может быть лучше, так как могут быть другие гифатизированные термины. Мне нужно сохранить –

ответ

4

Вы можете использовать этот PCRE регулярные выражения с глаголами (*SKIP)(*F) пропустить некоторые слова из вашего матча:

dvi-[id](*SKIP)(*F)|- 

RegEx Demo

Это будет пропускать слова dvi-i и dvi-d для расщепления за счет использования (*SKIP)(*F).

Для вашего кода:

$string =~ s/dvi-[id](*SKIP)(*F)|-/ /g; 

Perl Code Demo


Существует Н. альтернативных lookarounds на основе решения, а также:

/(?<!dvi)-|-(?![di])/ 

который в основном означает матча дефис, если это не предшествует dvi ИЛИ если за ним не следуют d или i, поэтому убедитесь, что не соответствуют -, если у нас есть dvi на LHS и [di] на RHS.

Perl код:

$string =~ s/(?<!dvi)-|-(?![di])/ /g; 

Perl Code Demo 2

+1

ваш демонстрационный вывод REGEX (** использовать либо '-dvi-d, либо-dvi-i **) не так, как ожидалось, , – Arijit

+0

Хорошая точка @ Arijit, теперь исправлено, – anubhava

+0

Не думаю, что я могу использовать (* SKIP) в perl, ему это не нравится? –

1
$string =~ s/(?<!dvi)-(?![id])|(?<=dvi)-(?![id])|(?<!dvi)-(?=[id])/ /g; 

При использовании только (?<!dvi)-(?![id]) вы также исключить dvi-x или x-i, где x может быть любым символом.

+1

что это? почему вы используете чередование? – rock321987

+0

Я добавил объяснение. – horcrux

+0

Я думаю, что OP рад исключить '-' между' dvi' и 'x'. – rock321987

-2

мы можем игнорировать конкретные слова, используя отрицательные упреждающую и отрицательного Look-за

Примера:

(?!pattern) 
is a negative look-ahead assertion 

в вашем случае картина

$string =~ s/(?<!dvi)-(?<![id])/ /g; 

выхода :

use either dvi-d or dvi-i 

Ссылка: http://www.perlmonks.org/?node_id=518444

Надеется, что это поможет.

+0

Он не разбивается на 'dvi-abc' – anubhava

+0

. Добавление' \ + 'решит вашу проблему. Ранее шаблон только для примера пользователя. – Arijit

+0

Снято '\ +' после '\ w' соответствует литералу' + ' – anubhava

0

Вряд ли вы можете получить простое и простое решение для регулярного выражения. Тем не менее, вы можете попробовать следующее:

#!/usr/bin/env perl 

use strict; 
use warnings; 

my %whitelist = map { $_ => 1 } qw(dvi-d dvi-i); 

my $string = 'use-either-dvi-d-or-dvi-i'; 

while ($string =~ m{ ([^-]+) (-) ([^-]+) }gx) { 
    my $segment = substr($string, $-[0], $+[0] - $-[0]); 
    unless ($whitelist{ $segment }) { 
     substr($string, $-[2], 1, ' '); 
    } 
    pos($string) = $-[ 3 ]; 
} 

print $string, "\n"; 

@- массив содержит начальные смещения соответствующих групп, а @+ массива содержит смещение концов. В обоих случаях элемент 0 относится ко всему матчу.

мне пришлось прибегнуть к чему-то вроде этого because of how \G works:

Отметим также, что s/// откажет перезаписать часть замещения, которая уже была заменена; так, например, это остановится после первой итерации, а не перебор своего пути в обратном направлении через строку:

$_ = "123456789"; 
    pos = 6; 
    s/.(?=.\G)/X/g; 
    print;  # prints 1234X6789, not XXXXX6789 

Может @tchrist может понять, как согнуть различные утверждения своей воли.