В чем разница между функцией exec () RegExp и функцией match () String?

122

Если я запустил это:

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

Я получаю это:

["a", "a"]

Но если я запустил это:

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

Тогда я получаю от этого ожидаемый результат:

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

Какая разница?

Джастин Варкентин
источник
4
вы выполняете цикл, execчтобы получить все подвыборки.
zzzzBov
2
Обратите внимание, что второй +вариант не нужен, поскольку matchон уже вернет все подвыражения. .execвозвращает только один каждый раз, так что это тоже не нужно +.
pimvdb
3
Вдобавок ко всему, вложенные квантификаторы, такие как два плюса, следует использовать чрезвычайно осторожно, потому что они легко приводят к катастрофическому возврату .
Мариус Шульц
1
@MariusSchulz Спасибо за ссылку. Это привело меня к изучению притяжательных кванторов и атомарной группировки. Очень приятные вещи для понимания.
Джастин Варкентин,

Ответы:

118

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 делает это за вас и отбрасывает захваченные группы.

Ry-
источник
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. До ES5 new RegExp("pattern")и /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))

Увидеть разницу?

Примечание. Чтобы выделить, обратите внимание, что захваченные группы (например: a, A) возвращаются после сопоставленного шаблона (например: aA), это не просто сопоставленный образец.

Georg
источник
28

/regex/.exec()возвращает только первое найденное совпадение, а "string".match()возвращает их все, если вы используете gфлаг в регулярном выражении.

Смотрите здесь: exec , match .

Алекс Чиминян
источник
23

Если ваше регулярное выражение глобальное и вы захватываете, вы должны использовать exec. Матч не вернет все ваши захваты.

Match отлично работает, когда просто сопоставляется (а не захватывается). Вы запускаете его один раз, и он дает массив всех совпадений. (хотя, если регулярное выражение не является глобальным, тогда match покажет совпадение, за которым следуют захваты)

Exec - это то, что вы используете при захвате, и каждый раз, когда он выполняется, он дает совпадение, а затем захват. (match будет вести себя таким образом, чтобы дать полное совпадение с последующими захватами, только если регулярное выражение не является глобальным).

Другое использование Exec - получение индекса или позиции совпадения. Когда у вас есть переменная для вашего регулярного выражения, вы можете использовать .lastIndex и получить позицию соответствия. Объект регулярного выражения имеет .lastIndex, а объект регулярного выражения - это то, что вы делаете .exec. Точечное совпадение выполняется в строке, и вы не сможете затем выполнить точку объекта регулярного выражения lastIndex

Строка имеет функцию сопоставления, которой передается регулярное выражение. И регулярное выражение имеет функцию exec и передается строка

exec вы запускаете несколько раз. матч вы бежите один раз

Хорошо использовать совпадение, когда не выполняется захват, а при захвате вы можете использовать exec, который является более мощным, поскольку он хорош для получения захватов, но если вы использовали совпадение при захвате, убедитесь, что оно показывает захваты, когда регулярное выражение не является глобальным, но не 'не показывать захваты, когда регулярное выражение является глобальным.

> "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 (имейте в виду, что, как вы можете видеть, с "index", "index", который он дает, действительно n-е вхождение, он отсчитывается от 1. Так что вы можете получить правильный index путем вычитания 1. И, как вы можете видеть, он дает 0 - lastIndex из 0 - не найдено).

И если вы хотите растянуть совпадение, вы можете использовать его при захвате, но не когда регулярное выражение является глобальным, и когда вы делаете это для этого, тогда содержимое массива не все совпадения, а полное матч с последующими захватами.

barlop
источник
Да, понимание работы r.lastIndexявляется ключевым фактором для понимания разницы между execи match.
Runun
@barlop "Матч не соответствует всем захватам", серьезно? «a, b, c, aa, bb, cc» .match (/ (\ w +) / g) => [«a», «b», «c», «aa», «bb», «cc» ]. Как объяснить, что он их все кешировал?
MrHIDEn
@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
@MrHIDEn Я бы не стал использовать тот же язык в неправильной цитате. И что важно, так это то, что показано и что мы можем видеть ... есть ли закулисное кеширование, тоже не имеет значения. И прошло некоторое время с тех пор, как я изучал это, но он не показывает все захваты .. Даже если вы сделаете свой пример, "a,b,c,aa,bb,cc".match(/(\w+)/g) что там происходит, он показывает все совпадения, и так уж получилось, что вы захватили каждое совпадение, поэтому если бы он показал все захваченные изображения, он бы выглядел точно так же (cntd)
barlop
(cntd) Так что, может быть, вы думаете, что он показывает захваты, но это не так, он показывает совпадения
barlop
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 () (сохраненное регулярное выражение + с использованием 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"]
ajax333221
источник
1

Иногда regex.exec () занимает много времени больше времени, чем string.match ().

Стоит отметить, что если результат string.match () и regex.exec () одинаков (например, когда не используется флаг \ g), regex.exec () займет где-то от x2 до x30, а затем string. соответствие():

Следовательно, в таких случаях подход «new RegExp (). Exec ()» следует использовать только тогда, когда вам нужно глобальное регулярное выражение (т.е. выполнить более одного раза).

dorony
источник
1
У вас есть эталон?
Sơn Trần-Nguyễn