web-dev-qa-db-fra.com

Des groupes de capture nommés dans une expression rationnelle JavaScript?

Autant que je sache, les groupes de capture nommés en JavaScript n'existent pas. Quelle est la méthode alternative pour obtenir une fonctionnalité similaire?

180
mmierins

ECMAScript 2018 introduit groupes de capture nommés dans les expressions rationnelles JavaScript.

Si vous devez prendre en charge des navigateurs plus anciens, vous pouvez tout faire avec des groupes de capture normaux (numérotés) que vous pouvez faire avec des groupes de capture nommés. changements de regex.

Les groupes de capture nommés auxquels je peux penser ne présentent que deux avantages "structurels":

  1. Dans certains types de regex (.NET et JGSoft, autant que je sache), vous pouvez utiliser le même nom pour différents groupes dans votre regex ( voir ici pour un exemple où cela compte ). Mais la plupart des versions regex ne supportent pas cette fonctionnalité de toute façon.

  2. Si vous devez faire référence à des groupes de capture numérotés dans une situation où ils sont entourés de chiffres, vous pouvez rencontrer un problème. Supposons que vous vouliez ajouter un zéro à un chiffre et donc que vous souhaitiez remplacer (\d) Par $10. En JavaScript, cela fonctionnera (tant que vous aurez moins de 10 groupes de capture dans votre expression rationnelle), mais Perl pensera que vous recherchez le numéro de référence 10 Au lieu du nombre 1, Suivi par un 0. En Perl, vous pouvez utiliser ${1}0 Dans ce cas.

En dehors de cela, les groupes de capture nommés ne sont que du "sucre syntaxique". Il est utile d’utiliser les groupes de capture uniquement lorsque vous en avez vraiment besoin et d’utiliser des groupes non-de capture (?:...) Dans tous les autres cas.

Le problème le plus important (à mon avis) avec JavaScript est qu’il ne prend pas en charge les expressions rationnelles prolixes, ce qui faciliterait beaucoup la création d’expressions régulières lisibles et complexes.

bibliothèque XRegExp de Steve Levithan résout ces problèmes.

96
Tim Pietzcker

Vous pouvez utiliser XRegExp , une implémentation augmentée, extensible, entre navigateurs, d'expressions régulières, incluant la prise en charge de la syntaxe, des indicateurs et des méthodes supplémentaires:

  • Ajoute une nouvelle syntaxe de regex et de texte de remplacement, y compris une prise en charge complète de nommée capture .
  • Ajoute deux nouveaux indicateurs de regex: s pour que les points correspondent à tous les caractères (mode dotall ou ligne unique) et x pour les espaces libres et les commentaires (mode étendu).
  • Fournit une suite de fonctions et méthodes qui facilitent grandement le traitement des regex complexes.
  • Corrige automatiquement les incohérences entre navigateurs les plus courantes en ce qui concerne le comportement et la syntaxe des expressions rationnelles.
  • Vous permet de créer et d’utiliser facilement des plug-ins qui ajoutent une nouvelle syntaxe et des indicateurs au langage d’expression régulière de XRegExp.
61
Yunga Palatino

Une autre solution possible: créer un objet contenant les noms de groupe et les index.

var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };

Ensuite, utilisez les clés d'objet pour référencer les groupes:

var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];

Cela améliore la lisibilité/qualité du code en utilisant les résultats de la regex, mais pas la lisibilité de la regex elle-même.

56
Mr. TA

Dans ES6, vous pouvez utiliser un tableau de déstructuration pour capturer vos groupes:

let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];

// count === '27'
// unit === 'months'

Remarquer:

  • la première virgule dans le dernier let ignore la première valeur du tableau résultant, qui correspond à la chaîne correspondante
  • le || [] après .exec() empêchera une erreur de déstructuration lorsqu'il n'y a pas de correspondance (car .exec() renverra null)
54
fregante

Mise à jour: Il a finalement été intégré à JavaScript (ECMAScript 2018)!


Les groupes de capture nommés pourraient très bientôt intégrer JavaScript.
La proposition est déjà à l'étape 3.

Un nom peut être attribué à un groupe de capture entre angular à l'aide de la syntaxe (?<name>...), pour tout nom d'identifiant. L’expression régulière d’une date peut alors être écrite sous la forme /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u. Chaque nom doit être unique et suivre la grammaire de ECMAScript IdentifierName.

Il est possible d'accéder aux groupes nommés à partir des propriétés d'une propriété groups du résultat de l'expression régulière. Des références numérotées aux groupes sont également créées, tout comme pour les groupes non nommés. Par exemple:

let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
16
Forivin

Nommer les groupes capturés fournit une chose: moins de confusion avec des expressions régulières complexes.

Cela dépend vraiment de votre cas d'utilisation, mais peut-être qu'une jolie impression de votre regex pourrait vous aider.

Vous pouvez également essayer de définir des constantes pour faire référence à vos groupes capturés.

Les commentaires peuvent alors aussi aider à montrer aux autres lecteurs de votre code ce que vous avez fait.

Pour le reste, je suis d’accord avec la réponse de Tims.

6
Yashima

Il existe une bibliothèque node.js appelée named-regexp que vous pouvez utiliser dans vos projets node.js (dans le navigateur en empaquetant la bibliothèque avec browserify ou d'autres scripts d'empaquetage). Cependant, la bibliothèque ne peut pas être utilisée avec des expressions régulières contenant des groupes de capture non nommés.

Si vous comptez les accolades de capture d'ouverture dans votre expression régulière, vous pouvez créer un mappage entre les groupes de capture nommés et les groupes de capture numérotés dans votre expression rationnelle, et vous pouvez mélanger et faire correspondre librement. Il vous suffit de supprimer les noms de groupe avant d'utiliser regex. J'ai écrit trois fonctions qui le démontrent. Voir ce résumé: https://Gist.github.com/gbirke/2cc2370135b665eee3ef

5
chiborg

Bien que vous ne puissiez pas faire cela avec Vanilla JavaScript, vous pouvez peut-être utiliser certains Array.prototype fonctionne comme Array.prototype.reduce pour transformer les correspondances indexées en désignations nommées en utilisant magic.

De toute évidence, la solution suivante nécessitera que les correspondances se produisent dans l'ordre:

// @text Contains the text to match
// @regex A regular expression object (f.e. /.+/)
// @matchNames An array of literal strings where each item
//             is the name of each group
function namedRegexMatch(text, regex, matchNames) {
  var matches = regex.exec(text);

  return matches.reduce(function(result, match, index) {
    if (index > 0)
      // This substraction is required because we count 
      // match indexes from 1, because 0 is the entire matched string
      result[matchNames[index - 1]] = match;

    return result;
  }, {});
}

var myString = "Hello Alex, I am John";

var namedMatches = namedRegexMatch(
  myString,
  /Hello ([a-z]+), I am ([a-z]+)/i, 
  ["firstPersonName", "secondPersonName"]
);

alert(JSON.stringify(namedMatches));
2
Matías Fidemraizer

Comme Tim Pietzcker, ECMAScript 2018 introduit les groupes de capture nommés dans les expressions rationnelles JavaScript. Mais ce que je n'ai pas trouvé dans les réponses ci-dessus, c'est comment utiliser le nommé groupe capturé dans la regex elle-même.

vous pouvez utiliser un groupe capturé nommé avec cette syntaxe: \k<name>. par exemple

var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/

et comme Forivin dit que vous pouvez utiliser le groupe capturé dans le résultat de l’objet comme suit:

let result = regexObj.exec('2019-28-06 year is 2019');
// result.groups.year === '2019';
// result.groups.month === '06';
// result.groups.day === '28';
  var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/mgi;

function check(){
    var inp = document.getElementById("tinput").value;
    let result = regexObj.exec(inp);
    document.getElementById("year").innerHTML = result.groups.year;
    document.getElementById("month").innerHTML = result.groups.month;
    document.getElementById("day").innerHTML = result.groups.day;
}
td, th{
  border: solid 2px #ccc;
}
<input id="tinput" type="text" value="2019-28-06 year is 2019"/>
<br/>
<br/>
<span>Pattern: "(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>";
<br/>
<br/>
<button onclick="check()">Check!</button>
<br/>
<br/>
<table>
  <thead>
    <tr>
      <th>
        <span>Year</span>
      </th>
      <th>
        <span>Month</span>
      </th>
      <th>
        <span>Day</span>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <span id="year"></span>
      </td>
      <td>
        <span id="month"></span>
      </td>
      <td>
        <span id="day"></span>
      </td>
    </tr>
  </tbody>
</table>
0
Hamed Mahdizadeh