Javascript - MultiLine RegExp:lastIndex卡在换行符上?

bra*_*rad 8 javascript regex

一些背景

来自Javascript:The Definitive Guide:

regexp但是,何时是全局正则表达式,exec()行为方式稍微复杂一些.它开始搜索stringlastIndexpreperty 指定的字符位置regexp.当找到匹配项时,它会设置lastIndex为匹配后第一个字符的位置.

我认为定期使用javascript RegExps的任何人都会认出这段话.但是,我在这个方法中发现了一个奇怪的行为.

问题

请考虑以下代码:

>> rx = /^(.*)$/mg

>> tx = 'foo\n\nbar'

>> rx.exec(tx)
[foo,foo]
>> rx.lastIndex
3
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
>> rx.exec(tx)
[,]
>> rx.lastIndex
4
Run Code Online (Sandbox Code Playgroud)

RegExp似乎卡在第二行,并没有增加lastIndex属性.这似乎与犀牛书相矛盾.如果我自己设置如下,它继续并最终返回null按预期,但似乎我不应该.

>> rx.lastIndex = 5
5
>> rx.exec(tx)
[bar,bar]
>> rx.lastIndex
8
>> rx.exec(tx)
null
Run Code Online (Sandbox Code Playgroud)

结论

显然,只要lastIndex匹配为空字符串,我就可以增加属性.然而,作为好奇的类型,我想知道为什么它不会被该exec方法增加.为什么不呢?

笔记

我在Chrome和Firefox中观察到了这种行为.它似乎只有在有相邻的换行符时才会发生.

[编辑]

Tomalak在下面说,改变模式/^(.+)$/gm将导致表达式不被卡住,但空行被忽略.这可以改为仍然匹配线?感谢Tomalak的答案!

[编辑]

使用以下模式并使用组1适用于我能想到的所有字符串.再次感谢Tomalak.

/^(.*)((\r\n|\r|\n)|$)/gm
Run Code Online (Sandbox Code Playgroud)

[编辑]

上一个模式返回空行.但是,如果您不关心空白行,Tomalak会给出以下解决方案,我认为该解决方案更清晰.

/^(.*)[\r\n]*/gm
Run Code Online (Sandbox Code Playgroud)

[编辑]

前两个解决方案都停留在尾随换行符上,因此您必须将它们剥离或lastIndex手动递增.

[编辑]

我发现了一篇很棒的文章,详细介绍lastIndexFlagrant Badassery的跨浏览器问题.除了令人敬畏的博客名称,这篇文章让我对这个问题有了更深入的了解,并提供了一个很好的跨浏览器解决方案.解决方案如下:

var rx = /^/gm,
    tx = 'A\nB\nC',
    m;

while(m = rx.exec(tx)){
    if(!m[0].length && rx.lastIndex > m.index){
        --rx.lastIndex;
    }

    foo();

    if(!m[0].length){
        ++rx.lastIndex;
    }
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*lak 7

问题在于点

^(.*)$
Run Code Online (Sandbox Code Playgroud)

与新行字符不匹配,但使用您的"m"开关制作"^""$"锚定到新行字符.这意味着之间的"一无所有" "\n",并"\n"可以与匹配成功"(.*)".

由于此匹配的宽度为零,因此该lastIndex属性无法前进.尝试:

^(.+)$
Run Code Online (Sandbox Code Playgroud)

编辑:要匹配空白行,请执行以下操作:

^(.*)\n?     // remove all \r characters beforehand
Run Code Online (Sandbox Code Playgroud)

要么

^(.*)(?:\r\n|\n\r|\n|\r)?  // all possible CR/LF combinations, but *once* at most
Run Code Online (Sandbox Code Playgroud)

......然后去比赛组1.