旗標 y
允許在原始字串的指定位置執行搜尋。
為了掌握 y
旗標的使用案例,以及更了解正規表示法的運作方式,讓我們來探討一個實際範例。
正規表示法的一項常見任務是「詞法分析」:我們取得一段文字,例如程式語言,並需要找出其結構元素。例如,HTML 有標籤和屬性,JavaScript 程式碼有函式、變數等等。
撰寫詞法分析器是一個特殊領域,有其專屬工具和演算法,因此我們不會深入探討,但有一個常見任務:在指定位置讀取某個項目。
例如,我們有一個程式碼字串 let varName = "value"
,而且我們需要從中讀取變數名稱,它從位置 4
開始。
我們將使用正規表示法 \w+
來尋找變數名稱。實際上,JavaScript 變數名稱需要更複雜的正規表示法才能準確配對,但這裡並不重要。
- 呼叫
str.match(/\w+/)
只會找到該行中的第一個字 (let
)。這不是我們要的。 - 我們可以新增旗標
g
。但呼叫str.match(/\w+/g)
會在文字中尋找所有字,而我們需要在位置4
的一個字。同樣地,這不是我們要的。
那麼,如何準確地在指定位置搜尋正規表示法?
讓我們嘗試使用函式 regexp.exec(str)
。
對於沒有旗標 g
和 y
的 regexp
,此函式只會尋找第一個配對,它與 str.match(regexp)
的運作方式完全相同。
…但如果存在旗標 g
,則它會從儲存在 regexp.lastIndex
屬性的位置開始在 str
中執行搜尋。而且,如果它找到配對,則會將 regexp.lastIndex
設定為配對後緊接的索引。
換句話說,regexp.lastIndex
用作搜尋的起點,每個 regexp.exec(str)
呼叫都會將其重設為新值(「最後一個配對之後」)。當然,這僅在存在 g
旗標時才會發生。
因此,連續呼叫 regexp.exec(str)
會一個接一個地傳回配對。
以下是一個此類呼叫的範例
let str = 'let varName'; // Let's find all words in this string
let regexp = /\w+/g;
alert(regexp.lastIndex); // 0 (initially lastIndex=0)
let word1 = regexp.exec(str);
alert(word1[0]); // let (1st word)
alert(regexp.lastIndex); // 3 (position after the match)
let word2 = regexp.exec(str);
alert(word2[0]); // varName (2nd word)
alert(regexp.lastIndex); // 11 (position after the match)
let word3 = regexp.exec(str);
alert(word3); // null (no more matches)
alert(regexp.lastIndex); // 0 (resets at search end)
我們可以在迴圈中取得所有配對
let str = 'let varName';
let regexp = /\w+/g;
let result;
while (result = regexp.exec(str)) {
alert( `Found ${result[0]} at position ${result.index}` );
// Found let at position 0, then
// Found varName at position 4
}
這種使用 regexp.exec
的方式是函式 str.matchAll
的替代方案,可以更進一步控制處理程序。
讓我們回到我們的任務。
我們可以手動將 lastIndex
設定為 4
,從指定位置開始搜尋!
如下所示
let str = 'let varName = "value"';
let regexp = /\w+/g; // without flag "g", property lastIndex is ignored
regexp.lastIndex = 4;
let word = regexp.exec(str);
alert(word); // varName
太棒了!問題解決了!
我們執行 \w+
的搜尋,從位置 regexp.lastIndex = 4
開始。
結果是正確的。
…但等等,別急。
請注意:regexp.exec
呼叫會從位置 lastIndex
開始搜尋,然後繼續進行。如果在位置 lastIndex
沒有字,但它在位置之後的某個地方,則會找到它
let str = 'let varName = "value"';
let regexp = /\w+/g;
// start the search from position 3
regexp.lastIndex = 3;
let word = regexp.exec(str);
// found the match at position 4
alert(word[0]); // varName
alert(word.index); // 4
對於某些任務,包括詞法分析,這根本是錯誤的。我們需要在文字中的指定位置準確找到配對,而不是在它之後的某個地方。這就是旗標 y
的用途。
旗標 y
會使 regexp.exec
準確地在位置 lastIndex
搜尋,而不是「從它開始」搜尋。
以下是使用標記 y
的相同搜尋
let str = 'let varName = "value"';
let regexp = /\w+/y;
regexp.lastIndex = 3;
alert( regexp.exec(str) ); // null (there's a space at position 3, not a word)
regexp.lastIndex = 4;
alert( regexp.exec(str) ); // varName (word at position 4)
正如我們所見,正規表示式 /\w+/y
沒有在位置 3
配對(與標記 g
不同),但會在位置 4
配對。
這不僅是我們需要的,使用標記 y
還有重要的效能提升。
想像一下,我們有一段很長的文字,而且裡面完全沒有配對。那麼使用標記 g
的搜尋會一直到文字的結尾,然後什麼都找不到,這會花費比使用標記 y
的搜尋更多時間,而後者只會檢查確切的位置。
在詞法分析等任務中,通常會在確切的位置進行許多搜尋,以檢查我們在那裡有什麼。使用標記 y
是正確實作和良好效能的關鍵。
留言
<code>
標籤,若要插入多行程式碼,請將它們包在<pre>
標籤中,若要插入超過 10 行程式碼,請使用沙盒(plnkr、jsbin、codepen…)