Поиск назад по регулярному выражению переменной длины

Мое регулярное выражение ниже:

(?<![\s]*?(\"|&quot;)")WORD(?![\s]*?(\"|&quot;))

Как видите, я пытаюсь сопоставить все экземпляры WORD, если только они не заключены в «кавычки». Так...

WORD <- Find this
"WORD" <- Don't find this
"   WORD   " <- Also don't find this, even though not touching against marks
&quot;WORD&quot;  <- Dont find this (I check &quot; and " so works after htmlspecialchars)

Я считаю, что мое регулярное выражение будет работать отлично, ЕСЛИ я не получил ошибку:

Compilation failed: lookbehind assertion is not fixed length

Есть ли способ сделать то, что я намереваюсь, учитывая ограничения просмотра назад?

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

Большое большое спасибо,

Мэтью

p.s. Раздел WORD фактически будет содержать детектор URL Джона Грубера.


person mrmrw    schedule 25.06.2013    source источник
comment
@Qtax упомянул хороший вопрос: вы пытаетесь чем-то заменить обнаруженные слова?   -  person FrankieTheKneeMan    schedule 25.06.2013
comment
Кроме того: я думаю, что регулярное выражение, которое вы пытались построить, больше похоже на: (?<!("|&quot;)\s*)WORD(?![\s]*(\"|&quot;)) нет необходимости делать ленивый захват пробела, и ваши термины пробела/кавычки были перевернуты в ретроспективном просмотре.   -  person FrankieTheKneeMan    schedule 25.06.2013


Ответы (2)


Я бы предложил другой подход. Это будет работать до тех пор, пока кавычки правильно сбалансированы, потому что тогда вы знаете, что находитесь внутри строки в кавычках, если число следующих кавычек нечетное, что делает ненужной часть просмотра назад:

if (preg_match(
'/WORD             # Match WORD
(?!                # unless it\'s possible to match the following here:
 (?:               # a string of characters
  (?!&quot;)       # that contains neither &quot;
  [^"]             # nor "
 )*                # (any length),
 ("|&quot;)        # followed by either " or &quot; (remember which in \1)
 (?:               # Then match
  (?:(?!\1).)*\1   # any string except our quote char(s), followed by that quote char(s)
  (?:(?!\1).)*\1   # twice,
 )*                # repeated any number of times --> even number
 (?:(?!\1).)*      # followed only by strings that don\'t contain our quote char(s)
 $                 # until the end of the string
)                  # End of lookahead/sx', 
$subject))
person Tim Pietzcker    schedule 25.06.2013
comment
А как насчет This string makes me mad. "word\""? - person FrankieTheKneeMan; 25.06.2013
comment
@FrankieTheKneeMan: Верно, это регулярное выражение не проверяет экранированные кавычки. При необходимости его можно модифицировать, чтобы обрабатывать их. - person Tim Pietzcker; 25.06.2013
comment
Фантастика, большое спасибо, я тестирую эту новинку в своем проекте, дам вам знать, как она работает - person mrmrw; 25.06.2013
comment
Кажется, это работает очень хорошо! Я обязательно проверю оба ответа, но пока все хорошо. Могу я спросить - для вашего ответа требуются модификаторы SX? Так как это может вызвать проблемы с моим существующим шаблоном. - person mrmrw; 25.06.2013
comment
Кроме того, единственный тип, который я обнаружил, не работает с вашим кодом, - это комбинация --- word word --- в той же строке, что и, к сожалению, в то время как второй правильно игнорируется, первый также который должен стать хитом - person mrmrw; 25.06.2013
comment
Моя ошибка - я просто неправильно оформил. Теперь он работает ОТЛИЧНО :) См. пример на myregextester.com/?r=cf653ff3. моего окончательного решения. Так благодарен за это, спасибо, спасибо, спасибо. Я действительно чувствую, что тоже чему-то научился. - person mrmrw; 25.06.2013
comment
@mrmrw: Приятно это слышать. Извините, что не ответила сразу, меня долго не было дома. Я думаю, вы нашли ответ на свой вопрос о модификаторах (модификатор s имеет значение только в том случае, если ваши строки в кавычках могут занимать несколько строк). - person Tim Pietzcker; 26.06.2013

Я бы предложил удалить строки в кавычках, а затем просмотреть то, что осталось.

$noSubs = preg_replace('/(["\']|&quot;)(\\\\\1|(?!\1).)*\1/', '', $target);
$n = preg_match_all('/\bWORD\b/', $noSubs, $matches);

Регулярное выражение, которое я использовал для замены строк в кавычках выше, рассматривает &quote;, " и ' как отдельные разделители строк. Для любого заданного разделителя ваше регулярное выражение выглядит примерно так:

/"(\\"|[^"])*"/

Итак, если вы хотите рассматривать &quot; как эквивалент ":

/("|&quot;)(\\("|&quot;)|(?!&quot;)[^"])*("|&quot;)/i

Если вы также хотите обрабатывать строки с одинарными кавычками (при условии, что нет слов с апострофами):

/("|&quot;)(\\("|&quot;)|(?!&quot;)[^"])*("|&quot;)|'(\\'|[^'])*'/i

Будьте осторожны при экранировании их для помещения в строки PHP.

РЕДАКТИРОВАТЬ

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

/("|&quot;)(\\("|&quot;)|(?!&quot;)[^"])*("|&quot;)|((?!"|&quot;).)+/i

В строки в кавычках и сегменты без кавычек, затем создайте новую строку с заменой, работающей только с разделами без кавычек:

$tokenizer = '/("|&quot;)(\\\\("|&quot;)|(?!&quot;)[^"])*("|&quot;)|((?!"|&quot;).)+/i';
$hasQuote = '/"|&quot;/i';
$word = '/\bWORD\b/';
$replacement = 'REPLACEMENT';
$n = preg_match_all($tokenizer, $target, $matches, PREG_SET_ORDER);
$newStr = '';
if ($n === false) {
    /* Print error Message */
    die();
}
foreach($matches as $match){
    if(preg_match($hasQuote, $match[0])){
        //If it has a quote, it's a quoted string.
        $newStr .= $match[0];
    } else {
        //Otherwise, run the replace.
        $newStr .= preg_replace($word, $replacement, $match[0]);
    }
}

//Now $newStr has your replaced String.  Return it from your function, or print it to
//your page.
person FrankieTheKneeMan    schedule 25.06.2013
comment
Это способ, но он не сработает, например, если вы хотите заменить эти кавычки чем-то. Кстати, я думаю, что '\\\1' имеет значение \\1, которое PCRE интерпретирует как соответствие простой обратной косой черте, за которой следует 1. Возможно, вы имели в виду '(?:\\\\.|(?!\1).)*'. - person Qtax; 25.06.2013
comment
Я так не думаю - строки с одинарными кавычками в php не допускают escape-последовательностей, кроме \'. Но я, честно говоря, не уверен. Позвольте мне проверить документы... (промежуток времени)... вы правы. php.net/manual/ ru/ '\'' или '\\'. Я обновлю это через минуту. - person FrankieTheKneeMan; 25.06.2013
comment
@Qtax - вы имеете в виду, хотели ли вы чем-то заменить обнаруженные слова? Вы можете легко создать более программный способ сделать это. Лично я бы разбил строку на подстроки в кавычках и не в кавычках, а затем запустил бы замену некавыченных битов. Хороший звонок - если это вариант использования, mrmrw, дайте нам знать. - person FrankieTheKneeMan; 25.06.2013
comment
Фантастика, большое спасибо, я тестирую эту новинку в своем проекте, дам вам знать, как она работает - person mrmrw; 25.06.2013
comment
Большое спасибо за ваш ответ - я думаю, что в этом случае ответ Тима - это тот, на который я пойду, поскольку я (хотя у меня много опыта работы с HTML/CSS/JS/PHP), довольно новичок в регулярном выражении, и Тим, возможно, немного проще для меня, чтобы следовать. Но все же СПАСИБО, СПАСИБО, СПАСИБО за ответ. Это удивительное место, и вы удивительный человек. Я намерен использовать свое свободное время, чтобы как можно чаще вносить свой вклад в stackoverflow. Так что еще раз спасибо. - person mrmrw; 25.06.2013