Регулярное выражение для сопоставления строк в одинарных кавычках не работает с PHP - PullRequest
0 голосов
/ 02 июля 2018

Итак, у меня есть это регулярное выражение:

/'((?:[^\\']|\\.)*)'/

Он должен совпадать со строками в одинарных кавычках, игнорируя при этом внутренние, экранированные одинарные кавычки \'

Здесь работает , но когда выполняется с PHP , я получаю разные результаты. Почему это?

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Это своего рода выход из ада. Несмотря на то, что уже есть принятый ответ, оригинальная модель на самом деле лучше. Зачем? Это позволяет избежать экранирующего символа, используя Метод развёртывания цикла, описанный Джеффри Фридлом в «Мастеринге регулярных выражений»: "([^\\"]*(?:\\.[^\\"]*)*)" (адаптировано для одинарных кавычек)

Демо

Развертывание цикла (с использованием двойных кавычек)

"                              # the start delimiter
 ([^\\"]*                      # anything but the end of the string or the escape char
         (?:\\.                #     the escape char preceeding an escaped char (any char)
               [^\\"]*         #     anything but the end of the string or the escape char
                      )*)      #     repeat
                             " # the end delimiter

Это не решает проблемы выхода из ада, но вы также были здесь:

Пример кода :

$re = '/\'([^\\\\\']*(?:\\\\.[^\\\\\']*)*)\'/';
$str = '\'foo\', \'can\\\'t\', \'bar\'
\'foo\', \' \\\'cannott\\\'\\\\\', \'bar\'
';

preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
var_dump($matches);
0 голосов
/ 02 июля 2018

Это может быть проще, если использовать отрицательный взгляд назад. Также обратите внимание, что вам нужно дважды экранировать косую черту - один раз, чтобы сообщить PHP, что вы хотите буквальную обратную косую черту, а затем еще раз сказать механизму регулярных выражений, что вы хотите буквальную обратную косую черту.

Обратите внимание также, что ваше выражение захвата (.*) является жадным - оно будет захватывать все, что находится между ' символами, включая другие ' символы, независимо от того, экранированы они или нет. Если вы хотите, чтобы он прекратился после первого неэкранированного ', используйте вместо него .*?. Я использовал не жадную версию в моем примере ниже.

<?php

$test = "This is a 'test \' string' for regex selection";
$pattern = "/(?<!\\\\)'(.*?)(?<!\\\\)'/";

echo "Test data: $test\n";
echo "Pattern:   $pattern\n";

if (preg_match($pattern, $test, $matches)) {
    echo "Matches:\n";
    var_dump($matches);
}
...