2010-10-27 23 views
0

有一個正則表達式:正則表達式返回在IE的值,「未定義」在Firefox和Safari /鉻

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

(它的設計解析CSS文件和「規則」是由JS生成)

當我在IE中嘗試這個時,所有的工作都應該如此。 同上,當我在RegexBuddy或正則表達式教練中嘗試它。

但是,當我在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和鉻的輸出:

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

當我嘗試使用string.match相同時,我得到一個數組在所有瀏覽器中都未定義,包括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; 

什麼髒話是怎麼回事?
我嘗試過的所有其他字符串都會導致第一組非捕獲。 任何幫助,非常感謝!

編輯: 在Mathhew的建議下,代碼被縮短了,許多小時的研究已經完成。
標題已更改爲使線程更容易找到。

我已經標記Mathew的答案是正確的,因爲它很好的研究和描述。
我在下面的回答(寫在Mathew修訂他之前)用更簡單和更直接的方式陳述邏輯。

+2

我建議您嘗試將其濃縮爲更小,更簡單的示例,該示例仍然表現出相同的差異。你甚至可以在這個過程中解決問題。 – 2010-10-27 22:24:19

+0

完成。現在更奇怪,因爲較小的代碼比原始代碼更不一致。 – SamGoody 2010-10-28 08:06:05

回答

1

IE錯了。在ECMAScript中,恰好有一種替代方法會導致字符串。所有其他必須是undefined(不是""或其他)。

因此,對於您的替代方案,包括(transform[^-][^;}]+)|(transform-origin[^;}]+),Firefox和Chrome在將失敗的捕獲設置爲undefined時正確無誤。

有一個在ECMAScript的5個標準爲例(§15.10.2.3)明確這一點:

注意的|正則表達式運算符 分開了兩種選擇。 模式首先嚐試匹配左側 替代方案(後面跟着正則表達式的 的續集);如果失敗, 它會嘗試匹配正確的 分隔符(後面跟着正則表達式的 的續集)。如果左側的 替代方案,右側的分割點, 和續集都有選擇點,則 後續的所有選項都會嘗試 ,然後繼續前進到 的下一個選項左側的替代方案。如果選擇 左邊的替代方式已用盡,則 右側的分離點將被替代,而不是 的左側替代方法。任何捕獲 模式中的一部分內部的圓括號|跳過|產生未定義的 值而不是字符串。

因此,對於 示例,/a|ab/.exec("abc「)返回 結果」a「而不是」ab「。此外, /((())(())((c)|(b))/.exec("abc「) 返回數組[」abc「,」a「,」a「, 未定義, 「BC」,未定義, 「BC」]和 不是[ 「ABC」, 「AB」,未定義, 「AB」, 「C」, 「C」,未定義]

編輯:我想通最後一部分。這適用於原始以及簡化版本。在這兩種情況下,rule1rule2都不能匹配;(在原始中,因爲;在否定字符類別[^;}]中)。因此,當聲明之間產生;時,交替選擇[^}]。因此,它必須將最後兩個捕獲設置爲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 " 

由於這是最後*,捕獲永遠不會被設置爲undefined。

+0

好的一點,雖然我真的不在乎這些迴應是未定義的還是空的,儘管我很在乎那些本應該被捕獲的結果不被忽視。 – SamGoody 2010-10-28 08:06:38

+0

謝謝,但我不認爲這有效。雖然 ;在否定字符類中,捕獲括號應該通過 - 但不包括分號。貪婪的明星也是如此。 如果您嘗試使用當前的模擬示例,即使您從字符串中刪除右大括號並允許捕獲一直走到最後,您將看到後面的括號不會捕獲任何內容。 – SamGoody 2010-10-28 10:06:59

+0

@Sam,它確實捕獲不包括分號,但是捕獲後來未被定義。我已經走過了上面的前三個例子。順便說一下,由於我們使用了很多示例,因此可以給它們賦予唯一的變量名稱以避免混淆。 – 2010-10-28 16:39:45

0

嘗試在上述正則表達式的第4行和第5行的前面刪除?:。我沒有測試過,但它看起來好像不屬於那裏。

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

所有這一切意味着他不想捕捉那個。 – 2010-10-27 22:34:10

+0

我知道這就是它的意思。它看起來應該捕捉它。 – 2010-10-27 22:34:45

+0

他們是故意的,但我刪除了它們,大大簡化了這個例子,就像在問題中一樣,它只是噪音。請再看一遍,我很惱火! – SamGoody 2010-10-28 08:12:54

0

你的第四和第五模式正在競爭。最終是由瀏覽器正則表達式引擎的實現來決定匹配。這不會是IE和其他人之間的第一個區別。

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

這兩種由transform前綴和通過origin後綴。你需要將它們凝聚成更簡潔的表達。類似以下內容的示例如下:

((?:-moz-)?(?:transfrom-origin[^;}]+)) 
+0

請備份您的觀點,即標準允許未定義的行爲。 – 2010-10-27 22:46:35

+0

@Matthew,我從我的答案中刪除了*未定義的行爲*,因爲我同意這可能是誤導。儘管如此,我認爲這至少是OP的問題的一部分。在回顧你的答案後,你似乎有相同的信念。 – 2010-10-28 01:52:17

+0

我也不認爲這是「取決於實施」。我只是覺得IE有一個bug。 – 2010-10-28 04:09:41

4

如何處理重複捕獲括號存在分歧。

的Firefox和Webkit都做如下假設,IE僅使得所述第一:

  1. 如果重複一個括號,捕捉每一次新的東西,只有最後的結果被存儲。
  2. 如果圓括號在更大的非捕獲重複圓括號內,並且不捕獲最後一個循環中的任何內容,則圓括號不會捕獲任何內容。

例如:

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

輕拍。exec會捕獲一個'a',然後用'b'等替換它,直到它返回一個'f'。
在所有瀏覽器中。

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

pat.exec將首先用'a','b','f'填充捕獲括號。
但是,非捕獲父母將繼續並匹配'g'。在此期間,沒有什麼可以進入捕捉括號,因此它被清空。
正則表達式將返回一個未定義的字符串作爲其響應。

IE認爲捕獲圓括號在最後一個循環中沒有捕獲任何東西,因此堅持使用'f'的最後一個有效響應。

這是有用的,但不合邏輯。

不合邏輯的用處比破壞性更有用。 (我們都討厭quirksmode。)
優勢Firefox/Chrome。

+0

是的,Firefox和Chrome遵循「任何捕獲括弧內部分模式的內容,而不是通過字符串生成未定義的值」。 (見上面的標準報價),IE不。 – 2010-10-28 16:46:43

2

測試情況下,能夠簡化例如爲:

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

這裏唯一的區別是,(?:(Foo)|Bar)atom在第二種情況下,這導致在其捕獲被清除重複(由quantifier)。

此行爲是由ECMAScript spec規定:

的RepeatMatcher的第4步每次重複的Atom時間清除Atom的捕獲。

從該規範IE的偏差也documented

ES3規定 「該RepeatMatcher的第4步清除Atom的捕獲每次重複原子時間」

每次Atom重複時,JScript都不會清除Atom的匹配項。


值得一提的是,ES規範是在與其他Perl-flavored正則表達式引擎的行爲,這通常表現像IE賠率:

的Chrome,火狐
"FooBar".match(/^(?:(Foo)|Bar)*/)[1] // => undefined 
的Perl
("FooBar" =~ m/^(?:(Foo)|Bar)*/)[0] # => "Foo" 
Python
re.match("^(?:(Foo)|Bar)*", "FooBar").group(1) # => "Foo" 
紅寶石
"FooBar".match(/^(?:(Foo)|Bar)*/)[1] # => "Foo" 
相關問題