RegExp的exec()函数和String的match()函数有什么区别?


122

如果我运行此命令:

/([^\/]+)+/g.exec('/a/b/c/d');

我得到这个:

["a", "a"]

但是如果我运行这个:

'/a/b/c/d'.match(/([^\/]+)+/g);

然后,我得到了预期的结果:

["a", "b", "c", "d"]

有什么不同?


4
您可以循环exec获取所有子选择。
zzzzBov 2012年

2
请注意,+不需要第二个,因为match它将已经返回所有子表达式。.exec每次仅返回一个,因此也不需要+
pimvdb '02

3
最重要的是,应特别小心地使用嵌套量词(如两个加号),因为它们很容易导致灾难性的回溯
Marius Schulz 2013年

1
@MariusSchulz感谢您的链接。这使我继续学习所有格量词和原子分组。很好理解的东西。
贾斯汀·沃肯汀

Answers:


117

exec带有全局正则表达式的表达式应在循环中使用,因为它仍将检索所有匹配的子表达式。所以:

var re = /[^\/]+/g;
var match;

while (match = re.exec('/a/b/c/d')) {
    // match is now the next match, in array form.
}

// No more matches.

String.match 为您执行此操作,并丢弃捕获的组。


39
我要在此答案中添加一些内容,不应将正则表达式文字放在while条件内,像这样while(match = /[^\/]+/g.exec('/a/b/c/d'),否则将创建无限循环!因为它是在MDN明确提出developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
yeyo

7
@yeyo:更具体地说,它必须是相同的正则表达式对象。文字不能达到目的。
Ry-

@ Ry-我想应该注意到这种行为是在ES5中引入的。new RegExp("pattern")与ES5之前的/pattern/含义不同。
罗伯特·

75

一张照片更好,你知道...

re_once = /([a-z])([A-Z])/
re_glob = /([a-z])([A-Z])/g

st = "aAbBcC"

console.log("match once="+ st.match(re_once)+ "  match glob="+ st.match(re_glob))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))

看到不同?

注意:要突出显示,请注意在匹配的模式(例如:aA)之后返回捕获的组(例如:a,A),而不仅仅是匹配的模式。



23

如果您的正则表达式是全局的,并且正在捕获,则必须使用exec。匹配不会返回您的所有捕获。

匹配非常适合仅匹配(不捕获)的情况。您运行一次,它将给出所有匹配项的数组。(尽管如果正则表达式不是全局的,则match将显示匹配项,然后是捕获)

Exec是捕获时使用的,每次执行时都会给出匹配项,然后是捕获。(只有当正则表达式不是全局的时,匹配才会以完全匹配后再捕获的方式运行)。

Exec的另一个用途是获取匹配项的索引或位置。当您的正则表达式具有变量时,可以使用.lastIndex并获取匹配项的位置。一个正则表达式对象具有.lastIndex,而正则表达式对象就是您对.exec执行的操作。点匹配是在字符串上完成的,您将不能再执行正则表达式对象点lastIndex

一个字符串,具有match函数,该函数传递一个正则表达式。还有一个正则表达式,具有exec函数,并传递了一个字符串

exec您运行了多次。匹配你跑一次

最好在不捕获时使用match,并且捕获时可以使用exec,它更强大,因为它对获取捕获很有用,但是如果在捕获时确实使用match,则看到它在正则表达式不是全局的时显示捕获。当正则表达式为全局时,不显示捕获。

> "azb".match(/a(z)b/);
[ "azb", "z" ]

> "azb".match(/a(z)b/g);
[ "azb" ]
>

另一件事是,如果您使用exec,请注意它是在正则表达式上调用的,那么,如果您为正则表达式使用了变量,则您将拥有更多的功能

当您不使用正则表达式变量时,您不会得到匹配项,因此当使用exec时,请使用变量用于正则表达式

> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
>
> /[a-c]/g.exec("abc")
[ "a" ]
> /[a-c]/g.exec("abc")
[ "a" ]
>

> var r=/[a-c]/g
> r.exec("abc")
[ "a" ]
> r.exec("abc")
[ "b" ]
> r.exec("abc")
[ "c" ]
> r.exec("abc")
null
>

使用exec,您可以获得比赛的“索引”

> var r=/T/g
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
2
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
6
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
9
> r.exec("qTqqqTqqTq");
null
> r.lastIndex
0
>

因此,如果您要索引或捕获,请使用exec(请记住,如您所见,使用“索引”,它给出的“索引”实际上是第n次出现,它是从1开始计数的。因此,您可以得出适当的减去1即可得到索引。如您所见,它给出0-lastIndex为0-未找到)。

而且,如果您想扩展匹配,则可以在捕获时使用它,但是当正则表达式是全局的时不能使用它,并且当您这样做时,数组的内容不是全部匹配,而是完整的匹配,然后是捕获。


是的,理解的工作r.lastIndex是理解的区别的关键因素execmatch
runun

@barlop“匹配不会匹配所有捕获”,认真吗?“ a,b,c,aa,bb,cc” .match(/(\ w +)/ g)=> [“ a”,“ b”,“ c”,“ aa”,“ bb”,“ cc” ]。如何解释它缓存了所有这些?
MrHIDEn '19

@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.我在控制台上找到了它。只需复制/粘贴"a,b,c,aa,bb,cc".match(/(\w+)/g);Opera,Firefox。
MrHIDEn's

@MrHIDEn我不会使用您在错误的报价中使用的语言。重要的是显示的内容以及我们能够看到的内容。幕后是否有任何缓存也无关紧要。自从我研究这个问题以来已经有一段时间了,但是它并没有显示所有捕获。.即使您进行了示例"a,b,c,aa,bb,cc".match(/(\w+)/g) ,正在发生的事情也显示了所有匹配,并且恰好您捕获了每个匹配,所以如果要显示所有捕获,它将看起来完全相同(cntd)
barlop

(cntd)因此,也许您认为它正在显示捕获,但事实并非如此,它正在显示比赛
barlop 19/09/25

6

.match()函数str.match(regexp)将执行以下操作:

  • 如果匹配的,则回复:
    • 如果在则表达式中使用了该g标志:它将返回所有子字符串(忽略捕获组)
    • 如果该g标志在正则表达式中使用:它将返回与regexp.exec(str)
  • 如果没有匹配,它将返回:
    • null

使用标志的.match()的示例g

var str = "qqqABApppabacccaba";
var e1, e2, e3, e4, e5;
e1 = str.match(/nop/g); //null
e2 = str.match(/no(p)/g); //null
e3 = str.match(/aba/g); //["aba", "aba"]
e4 = str.match(/aba/gi); //["ABA", "aba", "aba"]
e5 = str.match(/(ab)a/g); //["aba", "aba"] ignoring capture groups as it is using the g flag

.match()g标识等同于.exec()

e1=JSON.stringify(str.match(/nop/))===JSON.stringify(/nop/.exec(str)); //true
//e2 ... e4 //true
e5=JSON.stringify(str.match(/(ab)a/))===JSON.stringify(/(ab)a/.exec(str)); //true

.exec()函数regexp.exec(str)将执行以下操作:

  • 如果匹配的,则回复:
    • 如果在则表达式中使用了该g标志:它将返回(每次调用时):下一个匹配项:。重要提示:如果正则表达式对象未存储在变量中(它必须是同一对象),则它将不会进入下一个匹配项[N_MatchedStr, N_Captured1, N_Captured2, ...]N
    • 如果该g标志在正则表达式中使用:它将返回与具有g标志一样并且第一次被调用且仅被调用一次的结果。
  • 如果没有匹配,它将返回:
    • null

.exec()的示例(存储的regexp +使用g标志=每次调用都会更改):

var str = "qqqABApppabacccaba";
var myexec, rgxp = /(ab)a/gi;

myexec = rgxp.exec(str);
console.log(myexec); //["ABA", "AB"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //null

//But in this case you should use a loop:
var mtch, myRe = /(ab)a/gi;
while(mtch = myRe.exec(str)){ //infinite looping with direct regexps: /(ab)a/gi.exec()
    console.log("elm: "+mtch[0]+" all: "+mtch+" indx: "+myRe.lastIndex);
    //1st iteration = elm: "ABA" all: ["ABA", "AB"] indx: 6
    //2nd iteration = elm: "aba" all: ["aba", "ab"] indx: 12
    //3rd iteration = elm: "aba" all: ["aba", "ab"] indx: 18
}

的实例.exec()当它与每个呼叫改变:

var str = "qqqABApppabacccaba", myexec, myexec2;

//doesn't go into the next one because no g flag
var rgxp = /(a)(ba)/;
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
//... ["aba", "a", "ba"]

//doesn't go into the next one because direct regexp
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
//... ["ABA", "AB"]

1

有时regex.exec()将 更多的时间,然后string.match()。

值得一提的是,如果string.match()和regex.exec()的结果相同(例如:不使用\ g标志时),则regex.exec()的取值范围是x2到x30,然后是string。比赛():

因此,在这种情况下,仅当需要全局正则表达式(即执行多次)时,才应使用“ new RegExp()。exec()”方法。


1
你有基准吗?
儿子瀚阮
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.