Кратчайшие проблемы с матчем - PullRequest
1 голос
/ 22 апреля 2011

Я знаю? оператор включает «не жадный» режим, но я столкнулся с проблемой, я не могу обойтись. Рассмотрим такую ​​строку:

my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>';

, где есть открывающие и закрывающие теги <a> и </a>, есть клавиши ABC, DEF и GHI, но они окружены каким-то другим случайным текстом. Я хочу заменить <a>klashsdjDEFasl;jjf</a> на <b>TEST</b> например. Однако, если у меня есть что-то вроде этого:

$str =~ s/<a>.*?DEF.*?<\/a>/<b>TEST><\/b>/;

Даже с не жадными операторами. * ?, это не делает то, что я хочу. Я знаю, почему он этого не делает, потому что первый <a> соответствует первому вхождению в строке и полностью соответствует DEF, а затем соответствует ближайшему закрытию </a>. Однако мне нужен способ сопоставления ближайшего открытия <a> и закрытия </a> с "DEF". Так что в настоящее время я получаю это в результате:

<a>TEST</b><a>askldhsfGHIasfklhss</a>

Где, поскольку я ищу что-то, чтобы получить этот результат:

<a>sdkhfdfojABCasjklhd</a><b>TEST</b><a>askldhsfGHIasfklhss</a>

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

Спасибо, Эрик Сейферт

Ответы [ 5 ]

6 голосов
/ 22 апреля 2011
$str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/;

Проблема в том, что даже при не жадном сопоставлении Perl все еще пытается найти совпадение, которое начинается в самой левой точке строки.Так как .*? может соответствовать <a> или </a>, это означает, что он всегда найдет первый <a> в строке.

Добавление жадного (.*) в начале заставляет его найти последний возможное совпадение <a> в строке (поскольку .* сначала захватывает всю строку, а затем возвращается назад, пока не будет найдено совпадение).

Одно предупреждение: поскольку он находит самое правое совпадениеВо-первых, вы не можете использовать эту технику с модификатором /g.Любые дополнительные совпадения будут находиться внутри $1, а /g возобновит поиск, где закончилось предыдущее совпадение, поэтому он не найдет их.Вместо этого вам нужно использовать цикл вроде:

1 while $str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/;
2 голосов
/ 22 апреля 2011

Вместо точки, которая говорит: "соответствует любому символу" , используйте то, что вам действительно нужно, что говорит: "соответствует любому символу, который не является началом </a>" ,Это выглядит примерно так:

$str =~ s/<a>(?:(?!<\/a>).)*DEF(?:(?!<\/a>).)*<\/a>/<b>TEST><\/b>/;
0 голосов
/ 24 июля 2018

Насколько я понимаю, это то, что вы ищете.

Использование ленивых квантификаторов ? без глобального флага - вот ответ.

Например,

enter image description here

Если бы у вас был глобальный флаг /g, тогда он соответствовал бы всем совпадениям самой низкой длины, как показано ниже,enter image description here

0 голосов
/ 22 апреля 2011
s{
   <a>
   (?: (?! </a> ) . )*
   DEF   
   (?: (?! </a> ) . )*
   </a>
}{<b>TEST</b>}x;

Как правило,

(?: (?! PAT ) . )

является эквивалентом

[^CHARS]

для шаблонов регулярных выражений вместо символов.

0 голосов
/ 22 апреля 2011
#!/usr/bin/perl
use warnings;
use strict;

my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>';

my @collections = $str =~ /<a>.*?(ABC|DEF|GHI).*?<\/a>/g;

print join ", ", @collections;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...