регулярное выражение, проблема с обратной ссылкой в ​​шаблоне с preg_match_all

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

preg_match_all('/__\((\'|")([^\1]+)\1/', "__('match this') . 'not this'", $matches);

ожидается, что он будет соответствовать строке между __(''), но на самом деле он возвращает:

match this') . 'not this

Любые идеи?


person ahmedhelmy007    schedule 18.05.2011    source источник
comment
Действительно ли обратные ссылки работают в классах char?   -  person Qtax    schedule 19.05.2011
comment
извините, не хватает \ я исправил баттерн   -  person ahmedhelmy007    schedule 19.05.2011
comment
Переходим к следующему решению.   -  person    schedule 19.05.2011


Ответы (4)


Сделайте свое регулярное выражение нежадным:

preg_match_all('/__((\'|")([^\1]+)\1/U', "__('match this') . 'not this'", $matches)
person Arjan    schedule 18.05.2011
comment
Не используйте обратные ссылки в классах char. - person Qtax; 19.05.2011
comment
И что бы вы сделали, если бы строка для соответствия содержала экранированную кавычку? Вот так: __('совпадение с этим') . 'не это' :| - person jayarjo; 11.01.2013
comment
[^\1] соответствует любому символу, кроме символа с восьмеричным значением 1 - person Wiktor Stribiżew; 29.12.2019

Вы не можете использовать обратную ссылку внутри класса символов, потому что класс символов соответствует только одному символу, а обратная ссылка потенциально может соответствовать любому количеству символов или ни одному.

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

preg_match_all('/__\(([\'"])(?:(?!\1).)+\1\)/',
    "__('match this') . 'not this'", $matches);

Я также изменил ваше чередование - \'|" - на класс символов - [\'"] - потому что это намного эффективнее, и я экранировал внешние скобки, чтобы они соответствовали буквальным скобкам.


РЕДАКТИРОВАТЬ: Думаю, мне нужно расширить это «более эффективное» замечание. Я взял пример, который Фридл использовал для демонстрации этого момента, и протестировал его в RegexBuddy.

Применительно к целевому тексту abababdedfg
^[a-g]+$ сообщает об успехе после трех шагов, а
^(?:a|b|c|d|e|f|g)+$ требует 55 шагов.

И это при успешном совпадении. Когда я пробую это на abababdedfz,
^[a-g]+$ сообщает об ошибке после 21 шага;
^(?:a|b|c|d|e|f|g)+$ выполняет 99 шагов.

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

person Alan Moore    schedule 18.05.2011
comment
Действительно, намного больше? На сколько больше? - person Qtax; 19.05.2011
comment
Да, чередования очень медленные, чумы, которой следует избегать. Однако утверждения выполняются еще медленнее. - person ; 19.05.2011
comment
@sln: это зависит от того, насколько хорошо написано утверждение и как оно используется (как и сами регулярные выражения). В любом случае, гибкость, которую они обеспечивают, в большинстве случаев стоит снижения производительности. Но нет оправдания использованию чего-то вроде (a|b|c), если вместо него можно использовать [abc]. - person Alan Moore; 19.05.2011

Я удивлен, что это не дало вам сообщение об ошибке несбалансированной скобки.

 /
   __
   (
       (\'|")
       ([^\1]+)
       \1
 /

Этот [^\1] не будет брать содержимое буфера захвата 1 и помещать его в класс character
. Это то же самое, что и все символы, которые НЕ равны «1».

Попробуй это:

/__\(('|").*?\1\).*/

Вы можете добавить внутреннюю фиксирующую скобку, чтобы просто зафиксировать то, что находится между кавычками:
/__\(('|")(.*?)\1\).*/

Изменить: если внутренний разделитель не разрешен, используйте регулярное выражение Qtax.
Так как ('|").*?\1, даже если он не жадный, все равно будет соответствовать всем вплоть до замыкающего якоря. В данном случае __('all'this'will"match'), и лучше использовать ('[^']*'|"[^"]*) как

person Community    schedule 18.05.2011
comment
Технически я думаю, что \1 будет интерпретироваться в классе символов как символ с восьмеричным значением 1, но, тем не менее, точка, по сути, одна и та же (т.е. [^\1] не делает то, что думает ОП). Пример. - person eldarerathis; 19.05.2011

Вы можете использовать что-то вроде: /__\(("[^"]+"|'[^']+')\)/

person Qtax    schedule 18.05.2011
comment
Это предпочтительный метод, если внутренний разделитель не разрешен. - person ; 19.05.2011
comment
Просто отметим, что недостатком этого метода является невозможность захвата внутренних данных без включенного разделителя. - person ; 19.05.2011
comment
@sln: Конечно, вы можете это захватить; просто используйте разные группы для каждого подшаблона: ~__\(("(?<DQ>[^"']+)"|'(?<SQ>[^"']+)')\)~ - person Alan Moore; 19.05.2011
comment
@sln @Alan, или вы можете использовать (?|...), если ваш вариант поддерживает это. Например: /__\((?|"([^"]+)"|'([^']+)')\)/. Алан, я бы не стал использовать [^"'], весь смысл разных символов в кавычках в том, что вы можете использовать один внутри другого. ;) - person Qtax; 19.05.2011
comment
@Alan - я должен был сказать, что нет простого способа внутреннего захвата без логики постобработки независимых буферов захвата, чтобы определить, какой из них захвачен. - person ; 20.05.2011
comment
@Qtax - Сброс ветки (если доступен) является альтернативой, однако в движке есть особенности, которые заставляют меня опасаться их. Первоначально говоря, что это невозможно, для меня это означает необходимость некоторой постобработки, зависящей от платформы. То есть: возможные оговорки. - person ; 20.05.2011