标记 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` 标志,那么它将在 `str` 中执行搜索,从 `regexp.lastIndex` 属性中存储的位置开始。并且,如果它找到匹配项,则将 `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` 标志只检查确切的位置。
在词法分析等任务中,通常需要在特定位置进行多次搜索,以检查该位置的内容。使用标志 y
是实现正确且高效的代码的关键。
评论
<code>
标签,对于多行代码,请使用<pre>
标签,对于超过 10 行的代码,请使用沙箱(plnkr,jsbin,codepen…)