Яка різниця між функцією exec () RegExp від функції String's match ()?


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 робить це для вас і відкидає захоплені групи.


39
Мені є що додати до цієї відповіді, не слід розміщувати регулярний вираз у прямому значенні в режимі while, наприклад, while(match = /[^\/]+/g.exec('/a/b/c/d')це створить нескінченний цикл !. Як це чітко зазначено в MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo

7
@yeyo: Більш конкретно, він повинен бути тим самим об'єктом регулярного вираження. Буквал цього не досягає.
Ри-

@ 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), це не лише узгоджений шаблон.


28

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

Дивіться тут: exec , match .


23

Якщо ваш регекс є глобальним, а ви захоплюєте, тоді ви повинні використовувати exec. Матч не поверне всі ваші зйомки.

Матч чудово підходить для просто збігу (не для захоплення). Ви запускаєте його один раз, і він дає масив усіх збігів. .

Exec - це те, що ви використовуєте під час захоплення, і кожен раз, коли він виконується, він дає збіг, за яким слідує захоплення. (Матч буде вести себе таким чином, щоб отримати повний збіг з подальшими захопленнями, лише коли регулярний вираз не є глобальним).

Ще одне використання з Exec - отримання індексу або позиції відповідності. Коли у вас є змінна для вашого регулярного виразу, ви можете використовувати .lastIndex і отримати позицію відповідності. В об’єкті регулярного вираження є .lastIndex, а об'єктом регулярного вираження є те, що ви робите .exec. Збіг крапок проводиться на рядок, і ви не зможете потім зробити об'єкт regex dot lastIndex

Рядок має функцію відповідності, яка передається регулярним виразом. І регулярний вираз, має функцію 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 (майте на увазі, що, як ви бачите, з "індексом", "індекс", який він дає, - це дійсно п яте явище, він рахується від 1. Отже, ви можете отримати належне індекс, віднімаючи 1. І як ви бачите, він дає 0 - lastIndex 0 - для не знайдено).

І якщо ви хочете розтягнути відповідність, ви можете використовувати його під час зйомки, але не тоді, коли регулярний вираз є глобальним, і коли ви це зробите для цього, то вміст масиву - це не всі збіги, але є повним матч з подальшим захопленням.


Так, розуміння функціонування r.lastIndexє ключовим фактором для розуміння різниці між execта match.
Пробіг

@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)
барлоп,

(cntd) То, можливо, ви думаєте, що це показує захоплення, але це не так, це показ матчів
барлоп,

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матч. Важливо: він не перейде в наступний збіг, якщо об'єкт regexp не зберігається в змінній (він повинен бути тим самим об'єктом)
    • якщо 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, а потім рядок. match ():

Тому в таких випадках використання підходу "new RegExp (). Exec ()" слід використовувати лише тоді, коли вам потрібен глобальний регулярний вираз (тобто, щоб виконати більше одного разу).


1
У вас є орієнтир?
Sơn Trần-Nguyễn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.