Qual é a diferença entre a função exec () de RegExp e a função match () de String?

122

Se eu executar este:

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

Eu entendi isso:

["a", "a"]

Mas se eu executar isso:

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

Então, obtenho o resultado esperado:

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

Qual é a diferença?

Justin Warkentin
fonte
4
você faz um loop execpara obter todas as sub-seleções.
zzzzBov
2
Observe que o segundo +não é necessário, pois matchjá retornará todas as subexpressões. .execretorna apenas um de cada vez, então também não precisa disso +.
pimvdb
3
Além disso, os quantificadores aninhados como os dois pontos positivos devem ser usados ​​com extremo cuidado, pois podem facilmente levar a um retrocesso catastrófico .
Marius Schulz
1
@MariusSchulz Obrigado pelo link. Isso me levou a aprender sobre quantificadores possessivos e agrupamento atômico. Coisas muito boas de se entender.
Justin Warkentin

Respostas:

118

execcom uma expressão regular global deve ser usado em um loop, já que ainda recuperará todas as subexpressões correspondentes. Assim:

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 faz isso por você e descarta os grupos capturados.

Ry-
fonte
39
Tenho algo a acrescentar a esta resposta, não se deve colocar o literal de expressão regular dentro da condição while, assim while(match = /[^\/]+/g.exec('/a/b/c/d')ou criará um loop infinito !. Como está claramente declarado no MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
yeyo
7
@yeyo: Mais especificamente, deve ser o mesmo objeto de expressão regular. Um literal não faz isso.
Ry-
@ Ry- Acho que devemos observar que esse comportamento foi introduzido no ES5. Antes do ES5 new RegExp("pattern")e /pattern/significava coisas diferentes.
Robert
75

Uma foto é melhor, sabe ...

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

Veja a diferença?

Nota: Para realçar, observe que os grupos capturados (por exemplo: a, A) são retornados após o padrão correspondido (por exemplo: aA), não é apenas o padrão correspondido.

georg
fonte
28

/regex/.exec()retorna apenas a primeira correspondência encontrada, enquanto "string".match()retorna todas elas se você usar o gsinalizador na regex.

Veja aqui: exec , match .

Alex Ciminian
fonte
23

Se sua regex for global e você estiver capturando, deverá usar exec. Match não retornará todas as suas capturas.

A correspondência funciona muito bem quando apenas corresponde (não captura). Você o executa uma vez e fornece um array de todas as correspondências. (embora se a regex não for global, a correspondência mostrará a correspondência seguida por capturas)

Exec é o que você usa quando está capturando, e cada vez que é executado dá a correspondência, seguida pelas capturas. (a correspondência se comportará de forma a fornecer a correspondência completa seguida por capturas, apenas quando a regex não for global)

Outro uso com Exec é obter o índice ou posição de uma correspondência. Quando você tem uma variável para sua regex, pode usar .lastIndex e obter a posição da correspondência. Um objeto regex tem .lastIndex, e o objeto regex é o que você faz .exec. A correspondência de pontos é feita em uma string e você não poderá fazer o objeto regex ponto lastIndex

Uma string tem a função match, que é passada a um regex. E uma regex, tem a função exec, e é passada uma string

exec você executa várias vezes. jogo você corre uma vez

É bom usar match quando não estiver capturando e ao capturar você pode usar exec, que é mais poderoso porque é bom para obter capturas, mas se você usou match durante a captura, veja que mostra as capturas quando o regex não é global, mas não mostra capturas quando o regex é global.

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

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

Outra coisa é que se você usar exec, observe que é chamado na regex, então se você usou uma variável para a regex, você tem mais poder

Você não obtém as correspondências quando não usa a variável para o regex, então use a variável para o regex, ao usar 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
>

E com exec, você pode obter o "índice" da correspondência

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

Então, se você quiser índices ou captura, use exec (tenha em mente que, como você pode ver, com o "índice", o "índice" que ele dá é na verdade uma enésima ocorrência, está contando a partir de 1. Então você pode derivar o indexe subtraindo 1. E como você pode ver, dá 0 - lastIndex de 0 - para não encontrado).

E se você quiser esticar a correspondência, você pode usá-lo quando estiver capturando, mas não quando a regex for global, e quando você fizer isso para isso, o conteúdo da matriz não é todas as correspondências, mas sim o completo partida seguida pelas capturas.

barlop
fonte
Sim, entender o funcionamento de r.lastIndexé o fator chave para entender a diferença entre exece match.
executado em
@barlop "O jogo não vai combinar com todas as capturas", sério? "a, b, c, aa, bb, cc" .match (/ (\ w +) / g) => ["a", "b", "c", "aa", "bb", "cc" ] Como explicar que ele armazenou todos eles em cache?
MrHIDE em
@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.consegui no console. Basta copiar / colar o "a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox.
MrHIDE em
@MrHIDEn Eu não usaria a linguagem que você fez na sua citação errada. E o que importa é o que é mostrado e o que podemos ver .. se há algum cache nos bastidores também não é relevante. E já faz um tempo desde que eu olhei para isso, mas não mostra todas as capturas. Mesmo se você fizer o seu exemplo "a,b,c,aa,bb,cc".match(/(\w+)/g) O que está acontecendo lá, mostra todas as combinações, e acontece que você capturou todas as combinações, então se fosse para mostrar todas as capturas, seria exatamente o mesmo (cntd)
barlop
(cntd) Então talvez você esteja pensando que ele está mostrando as capturas, mas não está, está mostrando as partidas
barlop
6

A função .match ()str.match(regexp) fará o seguinte:

  • se não é um jogo que vai retornar:
    • se o gsinalizador for usado no regexp: ele retornará todas as substrings (ignorando grupos de captura)
    • se o gsinalizador não for usado no regexp: ele retornará o mesmo queregexp.exec(str)
  • se não houver correspondência, ele retornará:
    • null

Exemplos de .match () usando o gsinalizador:

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

E .match () sem o gsinalizador é equivalente a .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

A função .exec ()regexp.exec(str) fará o seguinte:

  • se não é um jogo que vai retornar:
    • se o gsinalizador for usado no regexp: ele retornará (a cada vez que for chamado) : [N_MatchedStr, N_Captured1, N_Captured2, ...]do próximoN partida. Importante: não avançará para a próxima partida se o objeto regexp não estiver armazenado em uma variável (precisa ser o mesmo objeto)
    • se o gsinalizador não for usado no regexp: ele retornará o mesmo como se tivesse umg sinalizador e fosse chamado pela primeira vez e apenas uma vez.
  • se não houver correspondência, ele retornará:
    • null

Exemplo de .exec () (regexp armazenado + usando o gsinalizador = ele muda a cada chamada):

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
}

Exemplos de .exec () quando não muda a cada chamada:

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

Às vezes, regex.exec () leva muito mais tempo do que string.match ().

Vale a pena mencionar que se o resultado de string.match () e regex.exec () for o mesmo (ex: quando não estiver usando o sinalizador \ g), regex.exec () levará em algum lugar entre x2 a x30 então string. Combine():

Portanto, em tais casos, o uso da abordagem de "new RegExp (). Exec ()" deve ser usado apenas quando você precisa de um regex global (ou seja, para executar mais de uma vez).

dorônia
fonte
1
Você tem um benchmark?
Sơn Trần-Nguyễn