¿Cómo capturar un número arbitrario de grupos en JavaScript Regexp?

81

Esperaría esta línea de JavaScript:

"foo bar baz".match(/^(\s*\w+)+$/)

para devolver algo como:

["foo bar baz", "foo", " bar", " baz"]

pero en su lugar devuelve solo la última coincidencia capturada:

["foo bar baz", " baz"]

¿Hay alguna forma de obtener todas las coincidencias capturadas?

disc0dancer
fuente

Respuestas:

90

Cuando repite un grupo de captura, en la mayoría de los sabores, solo se conserva la última captura; cualquier captura anterior se sobrescribe. De alguna manera, por ejemplo .NET, puede obtener todas las capturas intermedias, pero este no es el caso de Javascript.

Es decir, en Javascript, si tiene un patrón con N grupos de captura, solo puede capturar exactamente N cadenas por coincidencia, incluso si algunos de esos grupos se repitieron.

Entonces, en términos generales, dependiendo de lo que necesite hacer:

  • Si es una opción, divida en delimitadores
  • En lugar de coincidir /(pattern)+/, tal vez coincida /pattern/g, tal vez en un execbucle
    • Tenga en cuenta que estos dos no son exactamente equivalentes, pero puede ser una opción
  • Haga coincidir multinivel:
    • Captura el grupo repetido en un partido
    • Luego ejecuta otra expresión regular para romper esa coincidencia

Referencias


Ejemplo

Aquí hay un ejemplo de coincidencia <some;words;here>en un texto, usando un execbucle y luego dividiendo ;para obtener palabras individuales ( ver también en ideone.com ):

var text = "a;b;<c;d;e;f>;g;h;i;<no no no>;j;k;<xx;yy;zz>";

var r = /<(\w+(;\w+)*)>/g;

var match;
while ((match = r.exec(text)) != null) {
  print(match[1].split(";"));
}
// c,d,e,f
// xx,yy,zz

El patrón utilizado es:

      _2__
     /    \
<(\w+(;\w+)*)>
 \__________/
      1

Esto coincide <word>, <word;another>, <word;another;please>, etc. Grupo 2 se repite para capturar cualquier número de palabras, pero sólo pueden mantener la última captura. La lista completa de palabras es capturada por el grupo 1; esta cadena está entonces spliten el delimitador de punto y coma.

Preguntas relacionadas

poligenelubricantes
fuente
7

¿Qué tal esto? "foo bar baz".match(/(\w+)+/g)

meder omuraliev
fuente
Su código funciona, pero agregar una bandera global a mi ejemplo no resolverá el problema: "foo bar baz" .match (/ ^ (\ s * \ w +) + $ / g) devolverá ["foo bar baz"]
disc0dancer
funcionará si lo cambia a la expresión regular de @ Jet a continuación. "foo bar baz".match(/\w+/g) //=> ["foo", "bar", "baz"]. ignora la cadena coincidente en el frente, pero sigue siendo una alternativa razonable.
Jed Schneider
6

A menos que tenga un requisito más complicado sobre cómo está dividiendo sus cadenas, puede dividirlas y luego devolver la cadena inicial con ellas:

var data = "foo bar baz";
var pieces = data.split(' ');
pieces.unshift(data);
gddc
fuente
1
Este terminó siendo solo el consejo que necesitaba para despertarme al hecho de que, al menos para mi aplicación actual, no necesitaba nada más sofisticado que split ().
Hefesto
4

intente usar 'g':

"foo bar baz".match(/\w+/g)
Jet
fuente