Regex : capturer toutes les itérations d'un groupe capturant +

Regex : capturer toutes les itérations d'un groupe capturant + - Javascript/Node.js - Programmation

Marsh Posté le 18-02-2018 à 12:11:16    

Bonjour,
Je n'arrive pas à finaliser mon expression régulière, peut-être qu'il y aura un expert parmi vous qui saura m'aider  :sol: Voici formellement l'énoncé :
 

  • j'ai un string qui contient quelque chose de la forme : puissance=48MW/h
  • Je veux récupérer le libellé, le signe de comparaison, le nombre et tout le détail des unités, soit ici :
  • puissance, = , 48, M, W, / et enfin h
  • La première partie, aucun problème donc nous allons nous focaliser uniquement sur l'unité.
  • Pour simplifier, nous allons dire qu'il y a que 2 multiples c et m, qu'il y a que 4 unités possibles W, h, Z et y
  • Et pour bien visualiser quel groupe capture quoi, on va dire que les unités pouvant avant le slash seront W et h, être après le slash sont Z et y
  • Je ne double volontairement pas les slashs qui devraient l'être pour pouvoir tester facilement les expressions rationnelles dans https://regex101.com/


La première expression rationnelle qui vient est :  

Code :
  1. var regex = new RegExp("(c|m)?(h|W)+(?:([\/]?)(Z|y)+)?","ug" );


Testons là sur :

Code :
  1. mWh/Zy


 
Le problème est que comme le fait remarquer l'analyseur de RegEx https://regex101.com/:

Citation :


A repeated capturing group will only capture the last iteration. Put a capturing group around the repeated group to capture all iterations or use a non-capturing group instead if you're not interested in the data


 
Donc ce qu'il revient est très logiquement :
 

Code :
  1. Match 1
  2. Full match 0-6 `mWh/Zy`
  3. Group 1. n/a `m`
  4. Group 2. n/a `h`
  5. Group 3. n/a `/`
  6. Group 4. n/a `y`


 
Si j'applique la solution proposée par l'analyseur l'expression régulière devient :
 

Code :
  1. var regex = new RegExp("(c|m)?((?:h|W)+)(?:([\/]?)((?:Z|y)+))?","ug" );


 
et on obtient une fois appliqué sur le même string :
 

Code :
  1. Full match 0-6 `mWh/Zy`
  2. Group 1. n/a `m`
  3. Group 2. n/a `Wh`
  4. Group 3. n/a `/`
  5. Group 4. n/a `Zy`


 
Mais ce n'est toujours pas ce que je veux  :cry: , moi je veux  
 

Code :
  1. Full match 0-6 `mWh/Zy`
  2. Group 1. n/a `m`
  3. Group 2. n/a `W`
  4. Group 3. n/a `h`
  5. Group 4. n/a `/`
  6. Group 5. n/a `Z`
  7. Group 6. n/a `y`


 
Quelqu'un aurait une idée ?
 
Merci !  :bounce:
 
Edit : je me penche vers une analyse en plusieurs fois bouclant sur une application de RegExp.prototype.exec() (voir en bas de https://developer.mozilla.org/fr/do [...] egExp/exec), mais ça ne me satisfait pas des tonnes...


Message édité par naeco le 18-02-2018 à 12:16:03
Reply

Marsh Posté le 18-02-2018 à 12:11:16   

Reply

Marsh Posté le 18-02-2018 à 15:31:07    

Bonjour,
 
Pas tout compris à ton exemple simplifié alors je me suis permis d'en refaire un :
- première unité : "a" ou "b"
- multiplicateur de la première unité : "M" ou "k" ou aucun
- seconde unité : "x" ou "y"
- multiplicateur de la seconde unité : "m" ou "µ" ou aucun
 
Expression de la forme : [nom du résultat][signe de comparaison][nombre][multiplicateur de première unité optionnel][première unité][opérateur d'unité composée][multiplicateur de seconde unité optionnel][seconde unité]
 

Code :
  1. var str = "puissance=48Ma/x";
  2. var regex = /(\w+)(.)(\d+)(M|k)?(a|b)(.)?(m|µ)?(x|y)/;
  3. var result = regex.exec(str);
  4. console.log(result);


 
Mais j'ai du mal à comprendre ce que tu attends au niveau du signe de comparaison et de l'opérateur d'unité composée.
Tu auras vraiment autre chose que des "=" pour le signe de comparaison ? Genre des ">" ou même des ">=" ?
Pareil, pour l'unité tu attends toujours une unité composée ? Peux-tu avoir des unités composées avec des opérateurs implicites (i.e. "MWh" ) ?


---------------
C'est en écrivant n'importe quoi qu'on devient n'importe qui.
Reply

Marsh Posté le 18-02-2018 à 17:08:51    

Merci de ta réponse.
 
Oui tu as compris, juste un petit truc, dans une unité, il ne peut y avoir qu'un seul multiple, genre des newton-mètres peuvent être en mNm (mili Newton mètre, mais pas en mNmm (mill Newton milimètre)
 
Et oui, l'idée est de couvrir n'importe quelle combinaison d'unitée. Par exemple 1 J = 1 kg.m2.s-2 .
 
Donc il faut mettre un + après ton a|b et x|y, et là, c'est le drame car comme le dit la doc "A repeated capturing group will only capture the last iteration."...
 
Au final, j'ai avancé en le faisant en plusieurs fois : une fois l'unité isolée, je la sépare en deux s'il y a un signe divisé ou pas, et je traite les une ou deux parties ensuite.... C'est moins élégant que d'avoir réussi à faire une méga regex qui fait tout d'un coup, mais au final, ça fera du code peut être plus simple à relire...
 
Mais pour la démarche, la question est toujours d'actualité. Indépendamment du sujet, la question est :
 
Comment capturer toutes les itérations d'un groupe capturant "+"?


Message édité par naeco le 18-02-2018 à 17:09:17
Reply

Marsh Posté le 18-02-2018 à 17:32:45    

J'aurai utilisé un groupe npon capturant

Code :
  1. (\w+)([\>\<\=]+)([\d\.\,]+)((?:[pnµmkM]?[gmNsWh](?:-?\d+)?[\.\/]?)+)
 

En plus de détails:
- Capture de l'identifiant + comparaison + nombre (avec virgule ou point): (\w+)([\>\<\=]+)([\d\.\,]+)
- Capture de l'unité avec un groupe non capturant incluant les unités atomiques ((?:<unité atomique> )+)
- Ensuite définition d'une unité atomique: [pnµmkM]?[gmNsWh](?:-?\d+)?
- Et le séparateur d'unité éventuel: [\.\/]?

 

Cette regexp laissera passer plein de truc invalide, mais en supposant que ta chaine ne comporte pas nimp.


Message édité par h3bus le 18-02-2018 à 17:38:15

---------------
sheep++
Reply

Marsh Posté le 18-02-2018 à 19:07:50    

Le problème reste le même, comme dit, on ne peut pas capturer un nombre indéfini de motifs, c'est soit 0 soit 1 mais pas plus.
 
De toute façon je pense si c'était possible tu partirais sur une variable illisible. Je serai parti sur une capture des principaux éléments pour ensuite retravailler sur l'unité entière qui aura été capturée :

Code :
  1. function parse_datas(str) {
  2.   var regex1 = /(\w+)(.)(\d+)(.+)/;
  3.   var result = regex1.exec(str);
  4.   var resultat_regexp = {
  5.      'nom': result[1],
  6.      'comparateur': result[2],
  7.      'valeur': result[3],
  8.      'unite': result[4],
  9.   };
  10.   console.log(resultat_regexp);
  11.   var unite = result[4].split(/(\*|\.|\/)/);
  12.   console.log(unite);
  13.   var regex2 = /([a-zA-Z])?([a-zA-Z])\^?(-|\+)?(\d+)?/;
  14.   var sous_unites_brut = unite.map(val => regex2.exec(val)||val);
  15.   console.log(sous_unites_brut);
  16.   var sous_unites_corr = [];
  17.   sous_unites_brut.forEach(function(el,i){
  18.      if (Array.isArray(el)) {
  19.         sous_unites_corr[i] = {
  20.            'multiplicateur': el[1] === undefined? "":el[1],
  21.            'unite': el[2] === undefined? "":el[2],
  22.            'signe_exposant': el[3] === undefined? "":el[3],
  23.            'exposant': el[4] === undefined? "":el[4]
  24.         };
  25.      } else {
  26.         sous_unites_corr[i] = el;
  27.      }
  28.   });
  29.   console.log(sous_unites_corr);
  30.   console.log('Le multiplicateur de la première sous-unité (le "' + sous_unites_corr[0].unite + '" ) est "' + sous_unites_corr[0].multiplicateur + '", la troisième sous-unité (la "' + sous_unites_corr[4].unite + '" ) est élevée à la puissance "' + sous_unites_corr[4].signe_exposant + sous_unites_corr[4].exposant + '" alors que la seconde sous-unité (le "' + sous_unites_corr[2].unite + '" ) est élevé à la puissance "' + sous_unites_corr[2].signe_exposant + sous_unites_corr[2].exposant + '" mais a pour multiplicateur "' + sous_unites_corr[2].multiplicateur + '".');
  31. }
  32. parse_datas('puissance=48kg.mm2.s-2');


 
Normalement comme ça tu gardes les infos nécessaires à une éventuelle conversion et peux y accéder facilement.
 
EDIT : version plus propre...


Message édité par MaybeEijOrNot le 19-02-2018 à 11:33:35

---------------
C'est en écrivant n'importe quoi qu'on devient n'importe qui.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed