2015-07-02 2 views
4

Давайте следующий (немного сложно) регулярное выражение в JavaScript:JavaScript регулярное выражение неожиданное поведение

\{\{\s*(?:(?:\:)([\w\$]+))?\#(?:([\w\$\/][email protected]?)?([\s\S]*?))?(\.([\w\$\/]*))?\s*\}\} 

Я задаюсь вопросом, почему это соответствует вся строка здесь:

{{:control#}}x{{*>*}} 

, но не в в следующем случае (где после #) заполняется пробел:

{{:control# }}x{{*>*}} 

В PHP или Python, он соответствует в обоих случаях только первой части {{: ... }}.

Я хочу, чтобы JavaScript соответствовал только первой части. Возможно ли без взлома (?!}}) до [\s\S]?

Кроме того, является ли производительность причиной этого различного поведения в JavaScript или это просто ошибка в спецификации?

ответ

3

Вы можете использовать ленивый ?? квантор для достижения такого же поведения в JavaScript:

\{\{\s*(?:(?::)([\w$]+))?#(?:([\w$\/][email protected]?)?([\s\S]*?))??(\.([\w$\/]*))?\s*}} 
                ^^ 

См demo

От rexegg.com:

A??         Ноль или один A , ноль, если это еще разрешает общий шаблон matc ч (ленивый)

Это не ошибка, и она соответствует стандарту ECMA, который соответствует JavaScript.

Здесь, в (?:([\w$\/][email protected]?)?([\s\S]*?))?, у нас есть необязательная группа, не связанная с захватом, которая может соответствовать пустому тексту. JavaScript regex engine «потребляет» пустые тексты в необязательных группах, чтобы они могли быть позже доступны через обратные ссылки. Эта проблема тесно связана с Backreferences to Failed Groups. Например. ((q)?b\2) будет соответствовать b в JavaScript, но он не будет соответствовать Python и PCRE.

Согласно официальному стандарту ECMA, обратная ссылка на неучастную группу захвата должна успешно соответствовать ничему, как обратная ссылка на участвующую группу, которая ничего не зафиксировала.

+0

Почему это работает на PHP, Python или .NET без '?'? –

+0

Нет, '?' - жадный квантификатор на всех языках, я добавил более подробные ответы. –

0

Этот подшаблон отвечает за поведение:

([\w\$\/][email protected]?)? // P1 

как это соответствует жадности, вся тестовая строка (без места) получает потребляется.

Как указывает @stribizhev, отбор назначенной части вашего регулярного выражения для не-жадного соответствия приводит к консервативному совпадению.

Обе версии будут совпадать с #, так как оба шаблона совпадений содержат этот символ без каких-либо ограничений на вхождение.

Вторая тестовая строка (включая пробел после #) соответствует не жадному, так как P1 не соответствует белому пространству. Вместо этого это белое пространство получает матчед последующим подвыражением ([\s\S]*?), тем самым заканчивая матч.