Was ist der Unterschied zwischen der exec () - Funktion von RegExp und der match () - Funktion von String?

122

Wenn ich das mache:

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

Ich verstehe das:

["a", "a"]

Aber wenn ich das mache:

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

Dann bekomme ich das erwartete Ergebnis davon:

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

Was ist der Unterschied?

Justin Warkentin
quelle
4
Sie schleifen mit exec, um alle Unterauswahlen zu erhalten.
zzzzBov
2
Beachten Sie, dass der zweite +nicht benötigt wird, da matchalle Unterausdrücke bereits zurückgegeben werden. .execGibt jedes Mal nur eine zurück, daher wird dies auch nicht benötigt +.
Pimvdb
3
Darüber hinaus sollten verschachtelte Quantifizierer wie die beiden Pluspunkte äußerst vorsichtig verwendet werden, da sie leicht zu einem katastrophalen Backtracking führen .
Marius Schulz
1
@MariusSchulz Danke für den Link. Das brachte mich dazu, etwas über Possessivquantifizierer und Atomgruppierungen zu lernen. Sehr schöne Dinge zu verstehen.
Justin Warkentin

Antworten:

117

execmit einem globalen regulären Ausdruck soll in einer Schleife verwendet werden, da weiterhin alle übereinstimmenden Unterausdrücke abgerufen werden. So:

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 erledigt dies für Sie und verwirft die erfassten Gruppen.

Ry-
quelle
39
Ich muss dieser Antwort etwas hinzufügen. Man sollte das Literal des regulären Ausdrucks nicht in die while-Bedingung wie diese while(match = /[^\/]+/g.exec('/a/b/c/d')einfügen, da sonst eine Endlosschleife entsteht. Wie es in der MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo
7
@yeyo: Genauer gesagt muss es dasselbe Objekt mit regulären Ausdrücken sein. Ein Literal schafft das nicht.
Ry-
@ Ry- Ich denke, man sollte beachten, dass dieses Verhalten in ES5 eingeführt wurde. Vor ES5 new RegExp("pattern")und /pattern/bedeutete verschiedene Dinge.
Robert
75

Ein Bild ist besser, weißt du ...

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))

Sieh den Unterschied?

Hinweis: Beachten Sie zum Hervorheben, dass erfasste Gruppen (z. B. a, A) nach dem übereinstimmenden Muster (z. B. aA) zurückgegeben werden. Dies ist nicht nur das übereinstimmende Muster.

georg
quelle
28

/regex/.exec()Gibt nur die erste gefundene Übereinstimmung zurück, während "string".match()alle zurückgegeben werden, wenn Sie das gFlag in der Regex verwenden.

Siehe hier: exec , match .

Alex Ciminian
quelle
23

Wenn Ihre Regex global ist und Sie erfassen, müssen Sie exec verwenden. Match gibt nicht alle Ihre Aufnahmen zurück.

Match funktioniert hervorragend, wenn nur Matching (nicht Capturing). Sie führen es einmal aus und es gibt eine Reihe aller Übereinstimmungen. (Wenn der reguläre Ausdruck jedoch nicht global ist, zeigt die Übereinstimmung die Übereinstimmung an, gefolgt von den Erfassungen.)

Exec ist das, was Sie beim Erfassen verwenden, und jedes Mal, wenn es ausgeführt wird, gibt es die Übereinstimmung, gefolgt von den Erfassungen. (Die Übereinstimmung verhält sich so, dass die vollständige Übereinstimmung gefolgt von Erfassungen nur dann angezeigt wird, wenn der reguläre Ausdruck nicht global ist.)

Eine andere Verwendung mit Exec besteht darin, den Index oder die Position einer Übereinstimmung abzurufen. Wenn Sie eine Variable für Ihren regulären Ausdruck haben, können Sie .lastIndex verwenden und die Position des Abgleichs abrufen. Ein Regex-Objekt hat .lastIndex, und das Regex-Objekt ist das, worauf Sie .exec ausführen. Die Punktübereinstimmung wird für eine Zeichenfolge durchgeführt, und Sie können dann kein Regex-Objekt dot lastIndex ausführen

Ein String hat die Match-Funktion, der ein Regex übergeben wird. Und ein Regex hat die Exec-Funktion und erhält eine Zeichenfolge

exec Sie mehrmals ausführen. Match laufen Sie einmal

Es ist gut, Match zu verwenden, wenn Sie nicht erfassen, und wenn Sie erfassen, können Sie exec verwenden, das leistungsfähiger ist, da es gut zum Abrufen von Captures geeignet ist. Wenn Sie jedoch Match beim Capturing verwendet haben, sehen Sie, dass Captures angezeigt werden, wenn der reguläre Ausdruck nicht global ist, dies jedoch nicht Es werden keine Erfassungen angezeigt, wenn der reguläre Ausdruck global ist.

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

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

Eine andere Sache ist, dass wenn Sie exec verwenden, beachten Sie, dass auf dem regulären Ausdruck aufgerufen wird, und wenn Sie eine Variable für den regulären Ausdruck verwendet haben, Sie mehr Leistung haben

Sie erhalten keine Übereinstimmungen, wenn Sie die Variable nicht für den regulären Ausdruck verwenden. Verwenden Sie daher die Variable für den regulären Ausdruck, wenn Sie exec verwenden

> /./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
>

Und mit exec können Sie den "Index" des Spiels erhalten

> 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
>

Wenn Sie also Indizes oder Erfassungen wünschen, verwenden Sie exec (denken Sie daran, dass der "Index", wie Sie sehen, beim "Index" tatsächlich ein n-tes Vorkommen ist und von 1 zählt. Sie können also das richtige ableiten Index durch Subtrahieren von 1. Und wie Sie sehen können, gibt es 0 - lastIndex von 0 - für nicht gefunden).

Und wenn Sie Übereinstimmungen strecken möchten, können Sie sie beim Erfassen verwenden, aber nicht, wenn der reguläre Ausdruck global ist, und wenn Sie dies dafür tun, ist der Inhalt des Arrays nicht alle Übereinstimmungen, sondern vollständig Match gefolgt von den Captures.

Barlop
quelle
Ja, das Verständnis der Funktionsweise von r.lastIndexist der Schlüsselfaktor, um den Unterschied zwischen execund zu verstehen match.
Runsun
@barlop "Match passt nicht zu allen Captures", im Ernst? "a, b, c, aa, bb, cc" .match (/ (\ w +) / g) => ["a", "b", "c", "aa", "bb", "cc" ]. Wie kann man erklären, dass es alle zwischengespeichert hat?
MrHIDEn
@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.Ich habe es auf der Konsole. Kopieren Sie einfach "a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox.
MrHIDEn
@ MrHIDEn Ich würde nicht die Sprache verwenden, die Sie in Ihrem falschen Zitat verwendet haben. Und was zählt, ist, was gezeigt wird und was wir sehen können. Ob es hinter den Kulissen ein Caching gibt, ist ebenfalls nicht relevant. Und es ist schon eine Weile her, seit ich mich damit befasst habe, aber es werden nicht alle Erfassungen angezeigt. Selbst wenn Sie Ihr Beispiel machen "a,b,c,aa,bb,cc".match(/(\w+)/g) Was dort passiert, werden alle Übereinstimmungen angezeigt, und es kommt einfach so vor, dass Sie jede Übereinstimmung erfasst haben Wenn es alle Aufnahmen zeigen würde, würde es genau gleich aussehen (cntd)
barlop
(cntd) Vielleicht denken Sie, es zeigt die Erfassungen, aber es ist nicht so, es zeigt die Streichhölzer
Barlop
6

Die Funktion .match ()str.match(regexp) führt folgende Aktionen aus :

  • wenn es ist ein Spiel wird es zurück:
    • wenn das gFlag wird in der regexp verwendet: es wird wieder alle Teile (ohne Berücksichtigung Einfanggruppen)
    • Wenn das gFlag im regulären Ausdruck nicht verwendet wird, wird dasselbe wie zurückgegebenregexp.exec(str)
  • wenn es keine gibt Übereinstimmung gibt, wird Folgendes zurückgegeben:
    • null

Beispiele von .match () mit dem gFlag:

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

Und .match () ohne gFlag entspricht .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

Die Funktion .exec ()regexp.exec(str) führt folgende Aktionen aus :

  • wenn es ist ein Spiel wird es zurück:
    • wenn das gFlag wird in dem regulären Ausdruck verwendet: es wird wieder (für jedes Mal aufgerufen wird) : [N_MatchedStr, N_Captured1, N_Captured2, ...]beim nächsten NSpiel. Wichtig: Es wird nicht in die nächste Übereinstimmung übergehen, wenn das reguläre Ausdrucksobjekt nicht in einer Variablen gespeichert ist (es muss dasselbe Objekt sein).
    • wenn die gFlagge nicht ist im regulären Ausdruck verwendet wird: Es gibt dasselbe zurück, als hätte es ein gFlag und wurde zum ersten Mal und nur einmal aufgerufen.
  • Wenn es keine Übereinstimmung gibt, wird Folgendes zurückgegeben:
    • null

Beispiel für .exec () (gespeicherter regulärer Ausdruck + unter Verwendung des gFlags = ändert sich bei jedem Aufruf):

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
}

Beispiele für .exec (), wenn es sich nicht bei jedem Aufruf ändert:

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"]
ajax333221
quelle
1

Manchmal dauert regex.exec () viel länger als string.match ().

Es ist erwähnenswert, dass regex.exec () zwischen x2 und x30 liegt, wenn das Ergebnis von string.match () und regex.exec () identisch ist (z. B. wenn kein \ g-Flag verwendet wird). Spiel():

Daher sollte in solchen Fällen die Verwendung des Ansatzes "new RegExp (). Exec ()" nur verwendet werden, wenn Sie eine globale Regex benötigen (dh mehr als einmal ausführen müssen).

Dorony
quelle
1
Haben Sie einen Benchmark?
Sơn Trần-Nguyễn