javascript - Regex. Como obter múltiplas correspondências desta string?


Estou a usar regex em javascript. Digamos que tenho a seguinte string:

XXX_1_XXX XXX_2_XXX XXX_3_XXX YYY_1_YYY YYY_2_YYY YYY_3_YYY

e eu gostaria de executar uma regex e obter um resultado com este padrão:

Match1
1.    XXX_1_XXX
2.    YYY_1_YYY
Match2
1.    XXX_2_XXX
2.    YYY_2_YYY
Match3
1.    XXX_3_XXX
2.    YYY_3_YYY

Eu tentei variações disto:

/(XXX_(.)_XXX)(.)*?(YYY_\2_YYY)/g

mas ele para apenas na primeira correspondência.

Existe alguma forma de fazer isto com regex? Ou é melhor percorrê-lo como um array?

Resposta #1

Os matches são iterativos sobre a string, o regex apenas procura por mais matches após o fim do match anterior. Isto garante progresso, já que strings vazias podem resultar num ciclo infinito.

Mas podes resolver este problema da seguinte forma:

var text = "XXX_1_XXX XXX_2_XXX XXX_3_XXX YYY_1_YYY YYY_2_YYY YYY_3_YYY";
var re = /(XXX_(.)_XXX)(.)*?(YYY_\2_YYY)/;
while((m = re.exec(text)) !== null) {
    alert(JSON.stringify(m));//the result (print)
    //do something with m
    text = text.substring(m.index+1); //this is not the same as /g
    // "/g" would be text = text.substring(m.index+m[0].length+1);
}

O programa funciona da seguinte forma: você não utiliza o modificador /g, portanto apenas uma correspondência é feita.

  1. A cada iteração, você tenta combinar a string com a expressão regular.
  2. Caso corresponda, determina o .index onde a correspondência começa e elimina a string (inclusive) até esse ponto
  3. você repete a pesquisa com a cadeia modificada até que essa cadeia também não consiga encontrar convergência.

JSFiddle.

Nota: existe um caso em que isto pode falhar: se a string vazia também puder ser correspondida, pois no final da string continuará a corresponder à string vazia e o corte resultará noutra string vazia. No entanto, é fácil implementar uma verificação de comprimento zero. Este problema não se verifica com a resposta do @Ja͢ck.

Nota: outro aspeto que se deve ter em conta é que isto não requer uma progressão "global". A sequência XXX_1_XXX XXX_2_XXX XXX_3_XXX YYY_1_YYY YYY_3_YYY YYY_2_YYY (repare nos valores trocados na parte YYY_|_YYY), dará o mesmo resultado.

Resposta #2

O site regex101.com é um ótimo recurso para descobrir a expressão regular correta. Preparei dois exemplos:

/([XY]{3}_[0-3]_[XY]{3})/g

irá retornar:

CORRESPONDÊNCIA 1
1. [0-9] XXX_1_XXX
CORRESPONDÊNCIA 2
1. [10-19] XXX_2_XXX
CORRESPONDÊNCIA 3
1. [20-29] XXX_3_XXX
CORRESPONDÊNCIA 4
1. [30-39] YYY_1_YYY
CORRESPONDÊNCIA 5
1. [40-49] YYY_2_YYY
CORRESPONDÊNCIA 6
1. [50-59] YYY_3_YYY

https://regex101.com/r/xS9eA5/1

e

/(?:([XY]{3}_[0-3]_[XY]{3}) ([XY]{3}_[0-3]_[XY]{3}))/g

irá retornar:

JOGO 1
1. [0-9] XXX_1_XXX
2. [10-19] XXX_2_XXX
JOGO 2
1. [20-29] XXX_3_XXX
2. [30-39] YYY_1_YYY
JOGO 3
1. [40-49] YYY_2_YYY
2. [50-59] YYY_3_YYY

https://regex101.com/r/xS9eA5/2