Regex возвращает значение в IE, «неопределенное» в Firefox и Safari / Chrome - PullRequest
0 голосов
/ 28 октября 2010

Есть регулярное выражение:

.*?
(rule1|rule2)
(?:(rule1|rule2)|[^}])*

(Он предназначен для анализа файлов CSS, а JS генерирует «правила».)

Когда я пытаюсь это сделать в IE, все работает как надо. То же самое, когда я пробую это в RegexBuddy или The Regex Coach.

Но когда я пробую это в Firefox или Chrome, результаты пропускают значения.
Кто-нибудь может объяснить, что думают настоящие браузеры или как мне добиться результатов, похожих на IE?

Чтобы увидеть это в действии, загрузите страницу, которая дает вам интерактивное тестирование, например, пробный редактор W3Schools.

Вот источник, который можно вставить в: http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_regexp_exec

<html>
<body>

<script type="text/javascript">

var str="#rot { rule1; rule2; }";

var patt=/.*?(rule1|rule2)(?:(rule1|rule2)|[^}])*/i;

var result=patt.exec(str);
for(var i = 0; i < 3; i++) document.write(i+": " + result[i]+"<br>"); 

</script>
</body>
</html>

Вот вывод в IE:

0: #rot { rule1; rule2; 
1: rule1
2: rule2

Вот вывод в Firefox и Chrome:

0: #rot { rule1; rule2; 
1: rule1
2: undefined

Когда я пытаюсь сделать то же самое с помощью string.match, я получаю массив undefined во всех браузерах, включая IE.

var str="#rot { rule2; rule1; rule2; }";
var patt=/.*?(rule1|rule2)(?:(rule1|rule2)|[^}])*/gi;
var result=str.match(patt);
for(var i = 0; i < 5; i++) document.write(i+": "+result[i]+"<br>"); 

Насколько я могу судить, проблема в том, что последняя не записывающая скобка.
Когда я их удаляю, результаты совпадают между браузерами - и match () получает результаты.

Однако делает захват из последней скобки во всех браузерах, в следующем примере:

<script>
var str="#rot { rule1; rule2 }";
var patt=/.*?(rule1|rule2)(?:(rule1 |rule2 )|[^}])*/gi;
var result=patt.exec(str);
for(var i =0; i < 3; i++) document.write(i+": "+result[i]+"<br>"); 
</script>

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

var patt=/.*?(rule1|rule2)(?:(rule1[^1]|rule2[^1])|[^}])*/gi;

Что за ругательство происходит?!
Все другие строки, которые я пробовал, приводят к первому набору без уловов. Любая помощь с благодарностью!

EDIT: Код был сокращен, и по совету Мэтью было проведено много часов исследований.
Название было изменено, чтобы облегчить поиск темы.

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

Ответы [ 5 ]

4 голосов
/ 28 октября 2010

Существует разногласие в отношении обработки повторяющихся скобок захвата.

Firefox и Webkit делают следующие предположения, IE делает только первое:

  1. Если повторять скобки, захватывая каждый раз что-то новое, сохраняется только последний результат.
  2. Если круглые скобки находятся внутри большей, не повторяющейся, повторяющейся круглой скобки и ничего не записывают в последнем цикле, скобка не должна содержать ничего.

Например:

var str = 'abcdef';
var pat = /([a-f])+/;

pat.exec поймает «a», затем заменит его на «b» и т. Д., Пока не вернет «f».
Во всех браузерах.

var str = 'abcdefg';
var pat = /(?:([a-f])|g)+/;

pat.exec сначала заполняет круглые скобки с 'a', 'b', через 'f'.
Но родительский снимок будет продолжен и будет соответствовать букве «g». В течение которого нечего вводить в круглые скобки, поэтому он очищается.
И регулярное выражение вернет неопределенную строку в качестве ответа.

IE считает, что скобки захвата ничего не поймали в последней группе циклов, и поэтому придерживаются последнего действительного ответа 'f'.

Что полезно, но не логично.

Быть нелогичным полезно скорее разрушительно, чем полезно. (Мы все ненавидим причудливый режим.)
Преимущество Firefox / Chrome.

2 голосов
/ 18 сентября 2017

Контрольный пример может быть упрощен, например:

/^(?:(Foo)|Bar)(?:(Foo)|Bar)/.exec("FooBar") // => [ 'FooBar', 'Foo' ]
/^(?:(Foo)|Bar){2}/.exec("FooBar")           // => [ 'FooBar', undefined ]

Единственное отличие здесь состоит в том, что (?:(Foo)|Bar) атом равен , повторен (на квантификатор ) во втором случае, что приводит к очистке перехвата.

Такое поведение предусмотрено в спецификации ECMAScript :

Шаг4 из RepeatMatcher очищают захваты Atom каждый раз, когда Atom повторяется.

Отклонение IE от этой спецификации также задокументировано :

ES3 заявляет, что "Шаг 4 из RepeatMatcher очищает захваты Atom каждый раз, когда Atom повторяется. "

JScript не очищает совпадения Atom каждый раз, когда Atom повторяется.


Стоит отметитьчто спецификация ES противоречит поведению других движков со вкусом Perl, которые обычно ведут себя как IE:

Chrome, Firefox
"FooBar".match(/^(?:(Foo)|Bar)*/)[1] // => undefined
Perl
("FooBar" =~ m/^(?:(Foo)|Bar)*/)[0] # => "Foo"
Python
re.match("^(?:(Foo)|Bar)*", "FooBar").group(1) # => "Foo"
Ruby
"FooBar"[/^(?:(Foo)|Bar)*/, 1] # => "Foo"
1 голос
/ 28 октября 2010

IE не так. В ECMAScript только одна альтернатива может привести к строке. Все остальные должны быть undefined (не "" или что-то еще).

Таким образом, для ваших альтернатив, включая (transform[^-][^;}]+)|(transform-origin[^;}]+), Firefox и Chrome верны в установке неудачного захвата на undefined.

В стандарте ECMAScript 5 (§15.10.2.3) есть пример, касающийся этого:

ПРИМЕЧАНИЕ. | оператор регулярного выражения разделяет две альтернативы. шаблон сначала пытается соответствовать левому Альтернатива (с последующим продолжением регулярное выражение); если не получится, он пытается соответствовать правильному Дизъюнкция (с последующим продолжением регулярное выражение). Если слева Альтернатива, правильное дизъюнкция, и продолжение у всех есть точки выбора, все варианты в продолжении испробованы прежде чем перейти к следующему выбору в левая альтернатива. Если выбор в левая альтернатива исчерпана, вместо этого пробуется правильное дизъюнкция левой альтернативы. Любой захват скобки внутри части шаблон пропущен | производить неопределенный значения вместо строк.

Таким образом, для Например, /a|ab/.exec("abc ") возвращает результат «а», а не «ab». Более того, /((a)|(ab))((c)|(bc))/.exec("abc ") возвращает массив ["abc", "a", "a", undefined, "bc", undefined, "bc"] и not ["abc", "ab", undefined, "ab", "c", "c", не определено]

РЕДАКТИРОВАТЬ: Я понял последнюю часть. Это относится как к оригинальной, так и к упрощенной версии. В обоих случаях rule1 и rule2 не могут совпадать с ; (в оригинале, поскольку ; относится к классу отрицанных символов [^;}]). Таким образом, когда между объявлениями появляется ;, чередование выбирает [^}]. Таким образом, он должен установить последние два захвата на undefined.

Чтобы * был полностью жадным, конечный ; и пробел на входе также должны совпадать. Для последних двух * повторений (';' и '') чередование снова выбирает [^}], поэтому в конце также должны быть установлены захваты undefined.

IE не может сделать это в обоих случаях, поэтому они остаются равными "rule1" и "rule2".

Наконец, причина, по которой второй пример ведет себя по-разному, заключается в том, что (transform-origin[^;}]+)) соответствует самому последнему * повторению, поскольку до конца нет ;.

РЕДАКТИРОВАТЬ 2: Я буду проходить через то, что должно происходить в обоих текущих примерах. match - массив совпадений.

var str="#rot { rule1; rule2; }";
var patt=/.*?(rule1|rule2)(?:(rule1|rule2)|[^}])*/i;

.*? - "#rot { "

(rule1|rule2) - "rule1"
match[1] = "rule1"

Звезда 1

[^}] - ";"
match[2] = undefined 

Звезда 2

[^}] - " "
match[2] = undefined 

Звезда 3

(rule1|rule2) - "rule2"
match[2] = "rule2"

Звезда 4

[^}] - ";"
match[2] = undefined 

Звезда 5

[^}] - " "
match[2] = undefined 

Опять же, IE не устанавливает соответствие [2] на undefined.

Для примера str.match вы используете глобальный флаг. Это означает, что он возвращает массив совпадений без перехватов. Это относится к любому использованию String.match. Если вы используете g, вы должны использовать exec для получения снимков.

var str="#rot { rule1; rule2 }";
var patt=/.*?(rule1|rule2)(?:(rule1 |rule2 )|[^}])*/gi;

.*? - "#rot { "
(rule1|rule2) - "rule1"
match[1] = "rule1"

Звезда 1

[^}] - ";"
match[2] = undefined 

Звезда 2

[^}] - " "
match[2] = undefined 

Звезда 3

(rule1 |rule2 ) - "rule2 "
match[2] = "rule2 "

Поскольку это последний *, для захвата никогда не устанавливается неопределенное значение.

0 голосов
/ 28 октября 2010

Ваши 4-й и 5-й паттерны соревнуются.В конечном счете, все зависит от реализации механизма регулярных выражений браузеров.Это не было бы первым различием между IE и другими.

(?:(transform[^-][^;}]+)|(transform-origin[^;}]+))
(?:-moz-(?:(transform[^-][^;}]+)|(transform-origin[^;}]+))|[^}])*

Оба они имеют префикс transform и суффикс origin.Вы должны сжать это в более сжатое выражение.Пример примерно такой:

((?:-moz-)?(?:transfrom-origin[^;}]+))
0 голосов
/ 28 октября 2010

Попробуйте удалить ?: в начале строк 4 и 5 вашего регулярного выражения выше. Я не проверял это, но похоже, что они там не принадлежат.

(?:^|})
([^{]+)
[^}]+?-moz-
((transform[^-][^;}]+)|(transform-origin[^;}]+))
(-moz-(?:(transform[^-][^;}]+)|(transform-origin[^;}]+))|[^}])*
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...