Pregunta ¿Cómo se accede a los grupos coincidentes en una expresión regular de JavaScript?


Quiero unir una porción de una cadena usando un expresión regular y luego acceda a esa subcadena entre paréntesis:

var myString = "something format_abc"; // I want "abc"

var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);

console.log(arr);     // Prints: [" format_abc", "abc"] .. so far so good.
console.log(arr[1]);  // Prints: undefined  (???)
console.log(arr[0]);  // Prints: format_undefined (!!!)

¿Qué estoy haciendo mal?


Descubrí que no había nada de malo en el código de expresión regular anterior: la cadena real contra la que estaba probando era la siguiente:

"date format_%A"

Informar que "% A" no está definido parece un comportamiento muy extraño, pero no está directamente relacionado con esta pregunta, así que abrí uno nuevo, ¿Por qué una subcadena coincidente regresa "indefinida" en JavaScript?.


El problema era que console.log toma sus parámetros como un printf declaración, y desde la cadena que estaba registrando ("%A") tenía un valor especial, estaba tratando de encontrar el valor del siguiente parámetro.


1019
2018-01-11 07:21


origen


Respuestas:


Puede acceder a grupos de captura como este:

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
var match = myRegexp.exec(myString);
console.log(match[1]); // abc

Y si hay varias coincidencias, puede iterar sobre ellas:

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
match = myRegexp.exec(myString);
while (match != null) {
  // matched text: match[0]
  // match start: match.index
  // capturing group n: match[n]
  console.log(match[0])
  match = myRegexp.exec(myString);
}


1304
2018-01-11 07:26



Aquí hay un método que puede usar para obtener norteEl grupo de captura de cada partido:

function getMatches(string, regex, index) {
  index || (index = 1); // default to the first capturing group
  var matches = [];
  var match;
  while (match = regex.exec(string)) {
    matches.push(match[index]);
  }
  return matches;
}


// Example :
var myString = 'something format_abc something format_def something format_ghi';
var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;

// Get an array containing the first capturing group for every match
var matches = getMatches(myString, myRegEx, 1);

// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);


152
2018-01-08 08:26



var myString = "something format_abc";
var arr = myString.match(/\bformat_(.*?)\b/);
console.log(arr[0] + " " + arr[1]);

los \b no es exactamente lo mismo. (Funciona en --format_foo/, pero no funciona en format_a_b) Pero quería mostrar una alternativa a tu expresión, lo cual está bien. Por supuesto, el match la llamada es lo importante.


48
2018-01-11 09:10



Con respecto a los ejemplos de paréntesis de múltiples coincidencias anteriores, estaba buscando una respuesta aquí después de no obtener lo que quería:

var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);

Después de ver las llamadas a funciones ligeramente complicadas con while y .push (), me di cuenta de que el problema se puede resolver muy elegantemente con mystring.replace () en su lugar (el reemplazo NO es el punto, y ni siquiera está hecho , la opción de llamada de función recursiva LIMPIEZA incorporada para el segundo parámetro es!):

var yourstring = 'something format_abc something format_def something format_ghi';

var matches = [];
yourstring.replace(/format_([^\s]+)/igm, function(m, p1){ matches.push(p1); } );

Después de esto, no creo que vaya a utilizar .match () para casi nada nunca más.


19
2017-07-17 04:53



Tu sintaxis probablemente no sea la mejor para mantener. FF / Gecko define RegExp como una extensión de la función.
(FF2 fue tan lejos como typeof(/pattern/) == 'function')

Parece que esto es específico de FF: IE, Opera y Chrome arrojan excepciones para él.

En su lugar, use cualquiera de los métodos mencionados anteriormente por otros: RegExp#exec o String#match.
Ofrecen los mismos resultados:

var regex = /(?:^|\s)format_(.*?)(?:\s|$)/;
var input = "something format_abc";

regex(input);        //=> [" format_abc", "abc"]
regex.exec(input);   //=> [" format_abc", "abc"]
input.match(regex);  //=> [" format_abc", "abc"]

15
2018-01-11 12:55



Por último, encontré ese código de línea que funcionó bien para mí (JS ES6):

var reg = /#([\S]+)/igm; //get hashtags
var string = 'mi alegría es total! \n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';

var matches = (string.match(reg) || []).map(e => e.replace(reg, '$1'));
console.log(matches);

esto volverá: [fiestasdefindeaño, PadreHijo, buenosmomentos, france, paris]


9
2018-01-03 14:40



Terminología utilizada en esta respuesta:

  • Partido indica el resultado de ejecutar su patrón RegEx contra su cadena de esta manera: someString.match(regexPattern).
  • Patrones coincidentes indicar todas las partes coincidentes de la cadena de entrada, que todas residen dentro del partido formación. Estas son todas las instancias de su patrón dentro de la cadena de entrada.
  • Grupos coincidentes indique todos los grupos a capturar, definidos en el patrón RegEx. (Los patrones dentro de paréntesis, así: /format_(.*?)/g, dónde (.*?) sería un grupo coincidente). Estos residen dentro de patrones combinados.

Descripción

Para tener acceso a la grupos emparejados, en cada uno de los patrones combinados, necesitas una función o algo similar para iterar sobre el partido. Hay varias maneras de hacerlo, como muestran muchas de las otras respuestas. La mayoría de las otras respuestas usan un ciclo while para iterar sobre todo patrones combinados, pero creo que todos conocemos los peligros potenciales con ese enfoque. Es necesario hacer coincidir un new RegExp() en lugar de solo el patrón en sí, que solo se mencionó en un comentario. Esto es porque el .exec() método se comporta de manera similar a un función del generador - se detiene cada vez que hay un partido, pero mantiene su .lastIndex para continuar desde allí el próximo .exec() llamada.

Ejemplos de código

A continuación se muestra un ejemplo de una función searchString que devuelve un Array de todo patrones combinados, donde cada match es un Array con todo el contenido grupos emparejados. En lugar de usar un ciclo while, he proporcionado ejemplos utilizando tanto el Array.prototype.map() función, así como una forma más eficiente - usando un simple for-lazo.

Versiones concisas (menos código, más azúcar sintáctico)

Estos son menos efectivos ya que básicamente implementan un forEach-loop en lugar del más rápido for-lazo.

// Concise ES6/ES2015 syntax
const searchString = 
    (string, pattern) => 
        string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match => 
            new RegExp(pattern.source, pattern.flags)
            .exec(match));

// Or if you will, with ES5 syntax
function searchString(string, pattern) {
    return string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match =>
            new RegExp(pattern.source, pattern.flags)
            .exec(match));
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

Versiones perfectas (más código, menos azúcar sintáctico)

// Performant ES6/ES2015 syntax
const searchString = (string, pattern) => {
    let result = [];

    const matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (let i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
};

// Same thing, but with ES5 syntax
function searchString(string, pattern) {
    var result = [];

    var matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (var i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

Todavía tengo que comparar estas alternativas a las mencionadas anteriormente en las otras respuestas, pero dudo que este enfoque sea menos eficiente y menos a prueba de fallas que los demás.


7
2017-08-23 22:36



Un forro único que es práctico solo si tienes un par de paréntesis:

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};

5
2017-07-12 15:41



No hay necesidad de invocar el exec ¡método! Puede usar el método de "coincidencia" directamente en la cadena. Simplemente no te olvides de los paréntesis.

var str = "This is cool";
var matches = str.match(/(This is)( cool)$/);
console.log( JSON.stringify(matches) ); // will print ["This is cool","This is"," cool"] or something like that...

La posición 0 tiene una cadena con todos los resultados. La posición 1 tiene la primera coincidencia representada por paréntesis, y la posición 2 tiene la segunda coincidencia aislada entre paréntesis. Los paréntesis anidados son difíciles, ¡así que ten cuidado!


5
2018-06-19 19:47



Usando tu código:

console.log(arr[1]);  // prints: abc
console.log(arr[0]);  // prints:  format_abc

Editar: Safari 3, si es importante.


4
2018-01-11 07:27