web-dev-qa-db-fra.com

Comment accédez-vous aux groupes correspondants dans une expression régulière JavaScript?

Je veux faire correspondre une partie d'une chaîne en utilisant un expression régulière puis accéder à cette sous-chaîne entre parenthèses:

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'est-ce que je fais mal?


J'ai découvert qu'il n'y avait rien de mal avec le code des expressions rationnelles ci-dessus: la chaîne sur laquelle je testais était la suivante:

"date format_%A"

Signaler que "% A" n'est pas défini semble un comportement très étrange, mais il n'est pas directement lié à cette question. J'en ai donc ouvert un nouveau, Pourquoi une sous-chaîne correspondante renvoie-t-elle "non défini" en JavaScript?.


Le problème était que console.log prenait ses paramètres comme une instruction printf, et puisque la chaîne que je consignais ("%A") avait une valeur spéciale, elle essayait de trouver la valeur du paramètre suivant.

1130
nickf

Vous pouvez accéder aux groupes de capture comme ceci:

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

Et s'il y a plusieurs correspondances, vous pouvez les parcourir:

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

1437
CMS

Voici une méthode que vous pouvez utiliser pour obtenir le groupe de capture n e pour chaque correspondance:

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

165
Mathias Bynens

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

Le \b n'est pas exactement la même chose. (Cela fonctionne sur --format_foo/, mais pas sur format_a_b) Mais je voulais montrer une alternative à votre expression, ce qui est bien. Bien entendu, l’appel match est l’important.

51
PhiLho

En ce qui concerne les exemples de parenthèses multi-correspondances ci-dessus, je cherchais une réponse ici après ne pas obtenir ce que je voulais:

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

Après avoir examiné les appels de fonction légèrement compliqués avec while et .Push () ci-dessus, je me suis rendu compte que le problème peut être résolu très élégamment avec mystring.replace () à la place (le remplacement n'est PAS le point essentiel, et n'est même pas résolu , l’option d’appel de fonction récursive CLEAN pour le second paramètre est!):

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

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

Après cela, je ne pense plus jamais pouvoir utiliser .match () presque jamais.

25
Alexz

Votre syntaxe n'est probablement pas la meilleure à garder. FF/Gecko définit RegExp comme une extension de Function.
(FF2 est allé aussi loin que typeof(/pattern/) == 'function')

Il semble que cela soit spécifique à FF: IE, Opera et Chrome génèrent toutes des exceptions.

Utilisez plutôt l'une des méthodes précédemment mentionnées par d'autres: RegExp#exec ou String#match.
Ils offrent les mêmes résultats:

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"]
16
Jonathan Lonowski

Enfin, j’ai trouvé une ligne de code qui me convenait bien (JS ES6):

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

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

Cela retournera:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']
15
Sebastien H.

Terminologie utilisée dans cette réponse:

  • Match indique le résultat de l'exécution de votre modèle RegEx sur votre chaîne de la manière suivante: someString.match(regexPattern).
  • Matched patterns indique toutes les parties correspondantes de la chaîne d'entrée, qui résident toutes dans le tableau match. Ce sont toutes des instances de votre modèle à l'intérieur de la chaîne d'entrée.
  • Les groupes appariés indiquent tous les groupes à attraper, définis dans le modèle RegEx. (Les modèles entre parenthèses, comme suit: /format_(.*?)/g, où (.*?) serait un groupe correspondant.) Ceux-ci résident dans modèles correspondants.

La description

Pour accéder aux groupes correspondants, dans chacun des modèles correspondants, vous avez besoin d'une fonction ou de quelque chose de similaire pour effectuer une itération sur le correspondance. Vous pouvez le faire de différentes manières, comme le montrent de nombreuses autres réponses. La plupart des autres réponses utilisent une boucle while pour parcourir tous les motifs correspondants, mais je pense que nous connaissons tous les dangers potentiels de cette approche. Il est nécessaire de faire correspondre une new RegExp() à la place du modèle lui-même, qui n’a été mentionné que dans un commentaire. Cela est dû au fait que la méthode .exec() se comporte comme une fonction fonction générateur - elle s’arrête chaque fois qu’une correspondance est trouvée , mais conserve son .lastIndex pour continuer à partir du prochain appel .exec().

Exemples de code

Vous trouverez ci-dessous un exemple de fonction searchString qui retourne une Array de tous les modèles correspondants, où chaque match est une Array avec tous les groupes correspondants contenant. Au lieu d'utiliser une boucle while, j'ai fourni des exemples utilisant à la fois la fonction Array.prototype.map() et une méthode plus performante - en utilisant une boucle for- simple.

Versions concises (moins de code, plus de sucre syntaxique)

Celles-ci sont moins performantes puisqu'elles implémentent une boucle forEach- au lieu de la for- plus rapide.

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

Versions performantes (plus de code, moins de sucre syntaxique)

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

Je n'ai pas encore comparé ces alternatives à celles mentionnées précédemment dans les autres réponses, mais je doute que cette approche soit moins performante et moins fiable que les autres.

12
Daniel Hallgren

Il n'est pas nécessaire d'appeler la méthode exec! Vous pouvez utiliser la méthode "match" directement sur la chaîne. N'oubliez pas les parenthèses.

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 position 0 contient une chaîne avec tous les résultats. La position 1 a le premier match représenté par des parenthèses et la position 2 le deuxième match est isolé entre vos parenthèses Les parenthèses imbriquées sont délicates, alors méfiez-vous!

11
Andre Carneiro

Un seul support qui n'est pratique que si vous avez une seule paire de parenthèses:

while ( ( match = myRegex.exec( myStr ) ) && matches.Push( match[1] ) ) {};
7
Nabil Kadimi

En utilisant votre code:

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

Edit: Safari 3, si cela compte.

6
eyelidlessness

String#matchAll (voir la proposition de la phase 3 du 7 décembre 2018 ) simplifie l'accès à tous les groupes de l'objet de correspondance (sachant que le groupe 0 est le groupe entier). correspondance, tandis que les autres groupes correspondent aux groupes de capture du motif):

Avec matchAll disponible, vous pouvez éviter la boucle while et exec avec /g... À la place, en utilisant matchAll, vous récupérez un itérateur que vous pouvez utiliser avec le plus pratique for...of , tableau étendu , ou Array.from() constructions

Cette méthode génère une sortie similaire à Regex.Matches en C #, re.finditer en Python, preg_match_all en PHP.

Voir une démo de JS (testée dans Google Chrome 73.0.3683.67 (version officielle), bêta (64 bits)):

var myString = "key1:value1, key2-value2!!@key3=value3";
var matches = myString.matchAll(/(\w+)[:=-](\w+)/g);
console.log([...matches]); // All match with capturing group values

Les spectacles console.log([...matches])

enter image description here

Vous pouvez également obtenir une valeur de correspondance ou des valeurs de groupe spécifiques en utilisant

let matchData = "key1:value1, key2-value2!!@key3=value3".matchAll(/(\w+)[:=-](\w+)/g)
var matches = [...matchData]; // Note matchAll result is not re-iterable

console.log(Array.from(matches, m => m[0])); // All match (Group 0) values
// => [ "key1:value1", "key2-value2", "key3=value3" ]
console.log(Array.from(matches, m => m[1])); // All match (Group 1) values
// => [ "key1", "key2", "key3" ]

NOTE: voir les détails de compatibilité du navigateur .

5

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 = 'Rs.200 is Debited to A/c ...2031 on 02-12-14 20:05:49 (Clear Bal Rs.66248.77) AT ATM. TollFree 1800223344 18001024455 (6am-10pm)';
var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi;

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

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

5
Jack

Votre code fonctionne pour moi (FF3 sur Mac) même si je suis d'accord avec PhiLo que la regex devrait probablement être

/\bformat_(.*?)\b/

(Mais bien sûr, je ne suis pas sûr car je ne connais pas le contexte de la regex.)

2
PEZ
/*Regex function for extracting object from "window.location.search" string.
 */

var search = "?a=3&b=4&c=7"; // Example search string

var getSearchObj = function (searchString) {

    var match, key, value, obj = {};
    var pattern = /(\w+)=(\w+)/g;
    var search = searchString.substr(1); // Remove '?'

    while (match = pattern.exec(search)) {
        obj[match[0].split('=')[0]] = match[0].split('=')[1];
    }

    return obj;

};

console.log(getSearchObj(search));
1
Pawel Kwiecien

Nous pouvons accéder au groupe correspondant dans une expression régulière en utilisant une barre oblique inverse suivie du numéro du groupe correspondant:

/([a-z])\1/

Dans le code\1 représenté mis en correspondance par le premier groupe ([a-z])

1
Md. A. Barik

Vous n'avez pas vraiment besoin d'une boucle explicite pour analyser plusieurs correspondances - transmettez une fonction de remplacement comme deuxième argument, comme décrit dans: String.prototype.replace(regex, func) :

var str = "Our chief weapon is {1}, {0} and {2}!"; 
var params= ['surprise', 'fear', 'ruthless efficiency'];
var patt = /{([^}]+)}/g;

str=str.replace(patt, function(m0, m1, position){return params[parseInt(m1)];});

document.write(str);

L'argument m0 représente la sous-chaîne correspondante complète {0}, {1}, etc. m1 représente le premier groupe correspondant, c'est-à-dire la partie entre parenthèses dans l'expression régulière qui est 0 pour la première correspondance. Et position est l'index de départ dans la chaîne où le groupe correspondant a été trouvé - inutilisé dans ce cas.

1
ccpizza

Avec es2018, vous pouvez maintenant String.match() avec des groupes nommés, ce qui rend votre regex plus explicite sur ce qu’elle essayait de faire.

const url =
  'https://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression?some=parameter';
const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;
const { groups: segments } = url.match(regex);
console.log(segments);

et vous aurez quelque chose comme 

{protocole: "https", nom d'hôte: "stackoverflow.com", chemin: "questions/432493/comment-voulez-vous-accéder-aux-groupes-correspondants dans un expression régulière javascript", chaîne de requête: " un paramètre = "}

0
David Cheung

Obtenir toutes les occurrences du groupe

let m=[], s = "something format_abc  format_def  format_ghi";

s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g, (x,y)=> m.Push(y));

console.log(m);
0
Kamil Kiełczewski