Expressions régulières - Patterns pour RegExp

JNP

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re :),
C'est très instructif mais ça vole haut !
C'est surtout que le défi était très interressant : des formats de dates et de saisie de date très divers, avec obligation de récupérer la plus petite et la plus grande :p...
Et malgré mon appel, je n'ai pas vu de formulistes essayer de s'y confronter :(...
Mais je pense que ça peut te donner des idées ;)...
Bonne soirée :cool:
 

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re
@JM:)
Si je me trompe tu n'utilises pas Resultat, non ?
Si je l'utilise car cela me permet de vérifier dans la fenêtre des variables locales si la chaîne retourne un motif valide.
C'est vrai qu'une fois le motif au point, on peut s'en passer mais si je n'utilise pas Execute lorsque je mets au point le motif, je ne sais pas ce qui cloche (si par exemple la syntaxe du motif est bonne mais qu'il y a une parenthèse mal placée, c'est le résultat renvoyé par Execute qui me permet de renvoyer la chaîne de caractère capturée par rapport au motif).

J'ai le même problème avec l'instanciation de l'objet :Jean-Noël et toi, vous déclarez directement
With CreateObject("vbscript.regexp")
.....
End With

Le problème, c'est que si je n'instancie pas l'objet préalablement, je n'ai pas certaines infos dans ma fenêtre des variables locales et je n'arrive donc pas bien à comprendre d'où vient le problème au cas où,
alors que :
Set oRegExp = CreateObject("vbscript.regexp")
With oRegExp
.Pattern = "^(0[1-9]|[12]\d|3[01])/(01|0[3-9]|1[012])/(190[1-9]|19[1-9]\d|(20|21)\d\d)|(0[1-9]|[1]\d|[2][0-8])/02/(190[1-9]|19[1-9]\d|(20|21)\d\d)|29/02/((190|210)[48]|(19|20|21)([13579][26]|[2468][048])|200[048])$"
.Global = True
'Set Resultat = .Execute(MaChaine)
If .test(MaChaine) = True Then DateValide = True Else DateValide = False
End With
me permet d'accéder à des infos qui me permettent de comprendre les erreurs et d'arriver à mes fins.

Quant à l'utilisation de IIF (même si j'ai bien compris que ce n'était qu'un prétexte te permettant d'échanger;)), depuis que Hasco m'en a expliqué le fonctionnement et que j'ai lu que le temps de traitement était plus important (jusqu'à 6 fois plus selon les cas si je me souviens bien), je préfère utiliser une boucle IF...THEN classique.

@JNP:)
Et malgré mon appel, je n'ai pas vu de formulistes essayer de s'y confronter ...
Mais je pense que ça peut te donner des idées ...
Tu sais bien ce que j'en pense puisque nous en avons discuté:rolleyes:. Dans ce type de problème, rien ne vaut un bon vieux RegExp (et tu connais les arguments qui me font dire cela).

A+
 
Dernière édition:

Staple1600

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re



Quoi ?!?

Mon IIF ne serait donc que synonyme de SNIF :mad:

Je n'ai pas vu ce fil au Hasco dézingue mon IIF bien aimé.

Pour revenir à ton pattern, aurais-tu le temps de le détailler avec quelques explications, stp ?
 

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re JM
Ok.
Je te propose de disséquer sous-motif par sous-motif et quand tout te paraît claire, on passe au suivant.

Commençons par le sous-motif qui valide les années bissextiles, donc là où il existe un 29 février :
"29/02/((190|210)[48]|(19|20|21)([13579][26]|[2468][048])|200[048])"

le jour ne peut être qu'un 29 suivi d'une barre oblique (car chaîne de caractère recherchée au format jj/mm/aaaa, donc

Le mois ne peut être que celui de février suivi d'une barre oblique, donc

Les années :
définition des années bissextiles selon Wikipédia :
Depuis l'ajustement du calendrier grégorien, sont bissextiles les années :
1) soit divisibles par 4 mais non divisibles par 100
2) soit divisibles par 400.

Traitons dans un 1er temps le 1er cas dont le motif est (j'ai mis certains "|" en gras)
(190|210)[48]|(19|20|21)([13579][26]|[2468][048])
L'utilisation du "|" ="ou" et l'utilisation des parenthèses ( ) te permet de créer des masques.

Donc les "|" en gras te montrent qu'il existe 3 cas de figure :

- les années centenaires 1900 et 2100 non divisibles par 400 : dans les décennies 1900-1910 et 2100-2110, seules 1904, 1908, 2104 et 2108 sont bissextiles, donc
(190|210)[48]
, littéralement pour les années débutant par 190 ou ("|") 210, les années bissextiles se terminent par 4 ou 8 ([48], les crochets "[ ]" délimitent une classe de caractères).

- les années non centenaires dans les décennies impaires :
(19|20|21)([13579][26]
dont les 2 1er chiffres sont soit 19, soit 20 soit 21
. Le 3ème chiffres ne peut être que impaire, donc l'un de ceux compris dans la classe
et le 4ème 2 ou 6
(exemple des années bissextiles dans les décennies impaires : 1912 et 1916, 1932 et 1936,...)

- même principe pour les années non centenaires dans les décennies paires.

Maintenant, pour pouvoir raccourcir le motif, je me permets d'englober le traitement des décennies paires et impaires.
Pour cela, je définis d'abord les siècles concernés, soit les 19, 20 et 21ème :
Si la décennie de ces siècles est paire, le dernier chiffre sera obligatoirement 0, 4 et 8, et si la décennie est impaire, 2 et 6 (utilisation de "|" pour distinguer les 2 cas au sein du même masque délimité par un jeu de parenthèses).
Donc :
([13579][26]|[2468][048])

Le 1er jeu de parenthèses me permet de définir les siècles, puis j'inclus dans un 2ème jeu de parenthèses les 2 cas de figure (décennies paires se terminant par 2 ou 6, ou impaires se terminant par 0, 4 ou 8), donc :
(19|20|21)([13579][26]|[2468][048])


Le 2) sont les années centenaires divisibles par 400 : dans la plage qui nous intéresse (de 1901 à 2199), seule l'année 2000 répond à cette condition. Dans ces années-là, les années bissextiles sont 2000, 2004 et 2008, donc

Maintenant si tu as des questions par rapport à ce motif, pose-les.

Quand tout sera claire et si tu le veux, nous passerons au suivant.

Jean-Noël, si tu as quelque chose à ajouter ou si j'ai dis une bêtise, ne te gène pas pour intervenir.
A+
 
Dernière édition:

JNP

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re :),
Jean-Noël, si tu as quelque chose à ajouter ou si j'ai dis une bêtise, ne te gène pas pour intervenir.
Euh, j'ai pas tout testé :eek:...
Mais j'ai bien lu et le raisonnement me parait nickel ;)...
Ma seule remarque serait (mais plus pour les lecteurs du fil) que l'opérateur "ou", donc "|" s'exerce comme un ElseIf, donc, de gauche à droite à l'occidentale, et surtout, avec abandon de la première possibilité au fur et à mesure de la lecture du Pattern :rolleyes:...
A moins que je ne dise une bétise :eek:...
Bonne soirée :cool:
 

Staple1600

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Bonsoir

David84
Explications limpides.
Pattern compris.
Chapeau bas.

Au suivant ;)

JNP
Tu pourrais développer ton :rolleyes:..., stp.
 
Dernière édition:

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re JM
OK.
Intéressons-nous maintenant à celui gérant les mois de février classiques :
(0[1-9]|[1]\d|[2][0-8])/02/(190[1-9]|19[1-9]\d|(20|21)\d\d)

La partie des jours est définie par la partie
((0[1-9]|[1]\d|[2][0-8])|[1]\d|[2][0-8])
Donc 3 possibilités délimitées par le "|" (ou) enfermé dans un jeu de parenthèses (le masque) : les jours peuvent aller :

1) de 01 à 09
donc littéralement la syntaxe comporte un 0 suivi de l'un des chiffres de la classe 1 à 9

2) de 10 à 19
, donc le chiffres commence automatiquement par 1 et est suivi d'un autre chiffre compris entre 0 (10) et 9 (19). Tu remarqueras que contrairement à la partie 01-09 où le 2ème chiffre ne peut pas être 0, là le 0 fait partie de cette classe, d'où l'utilisation du \d (barre oblique suivie d'un digit (nombre allant de 0 à 9).
J'aurai également pu écrire cette partie [0-9] car les 2 veulent dire la même chose.

3) de 20 à 28, donc même principe, d'où le
.

On place ensuite la barre permettant de séparer les jours du mois
(à noter que j'aurai pu (dû ?) l'écrire
, \/ voulant dire "le caractère /"

Concernant le mois, un seul est concerné, donc

Concernant les années, cette partie est validée par
(190[1-9]|19[1-9]\d|(20|21)\d\d)
Créons un masque pour gérer les alternatives des siècles (toujours le même principe : des "|" séparant les différentes alternatives, le tout isolé par un jeu de parenthèses) :
(190[1-9]|19[1-9]\d|(20|21)....)

3 alternatives à gérer :
1) l'année 1901
2) les autres années de ce siècle
3 les 21ème et 22ème siècles

1) Le 20ème siècle est amputé de l'année 1900 pour des raisons de défaut de traitement d'Excel, donc on l'écarte par
(de 1901 à 1909)

2) les autre décennies de ce siècle :
, littéralement, le siècle
suivi de la décennie allant de1910 à 1990, donc
.
Le 4ème chiffre est compris entre 0 et 9, donc
.

3) les autres siècles sont plus simple à gérer puisque toutes les années sont prises en compte : un masque permet d'isoler les 2 cas
suivi de 2 digits pour les 2 derniers chiffres
que j'aurais pu également écrire
ou encore
NB : les accolades permettent de délimiter des intervalles de reconnaissance. Ces intervalles sont sont placés derrière le caractère à traités (ils sont dits "postfixés").

Ci-joint les explications que tu peux trouver ici :

{n} signifie "exactement n occurrences"
Exemple : Le motif "a{3}" permet de décrire la chaîne "aaa", et rien d'autre !

{n, p} signifie "entre n et p occurrences"
Exemple : Le motif "a{1, 3}" permet de décrire les chaînes "a", "aa" et "aaa"

{n,} signifie "au moins n occurrences"
Exemple : Le motif "a{5,}" permet de décrire les chaînes "aaaaa", "aaaaaa", "aaaaaaa", etc...

# Observation pour conclure :
Ici aussi, les intervalles de reconnaissance sont postfixés.
Tu peux également te référer au fichier produit par Jean-Noël qui t'explique la syntaxe d'une expression rationnelle.

Il ne te reste plus enfin qu'à préciser les 2 derniers chiffres qui peuvent aller de 0 à 9 (de 2000 à 2099 et de 2100 à 2199) :
Remarque bien que je place ces 2 digits après la parenthèse fermante du masque gérant les siècles
car ces 2 siècles sont concernées par ce cas de figure.

Cette partie donne au final
190[1-9]|19[1-9]\d|(20|21)\d\d
auquel je rajoute le jeu de parenthèses entrante et sortante du masque permettant la gestion des alternatives de la partie "année" de ce motif.
(190[1-9]|19[1-9]\d|(20|21)\d\d)

Dis-moi si tu as des questions ou si l'on passe au dernier sous-motif.

A+
 
Dernière édition:

JNP

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re :),
JNP
Tu pourrais développer ton :rolleyes:..., stp.
C'est pas évident, il faudrait faire des tests, mais si j'avais bien compris ce que j'avais lu, il faut être prudent avec le | car en gros, s'il a trouvé la partie gauche, il ne cherche plus la partie droite pour cette occurence (comme un test "si" qui n'analyse pas la suite une fois la condition remplie), ce qui en général ne pose pas de problème, mais peux en poser dans certains cas particuliers :rolleyes:...
Malheureusement, je ne me souviens plus où je l'avais lu, ni des cas particuliers :eek:...

On place ensuite la barre permettant de séparer les jours du mois (à noter que j'aurai pu (dû ?) l'écrire , \/ voulant dire "le caractère /"
Le slash n'est pas un caractère syntaxique (donc interdit) dans RegExp, donc il ne nécessite pas de \ devant :p...
Par contre, ce que j'avais lu, c'est que \ est ce qu'on appelle un caractère d'échappement, pour que le caractère suivant utilisé dans la syntaxe soit pris pour lui même et non comme élément syntaxique, exemple \. qui représentera un "point" et non "n'importe quel caractère" dans le masque.
Il sert aussi à définir les caractères par leurs codes, en octal (\0), en exa (\x) ou en ASCII (\u).
Du fait, je pense que le \ de \/ ne devrait pas être pris en compte et simplement ignoré dans le pattern, mais tant qu'à faire, j'éviterais :rolleyes:.

Bonne suite :cool:
 

Staple1600

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Bonsoir

David84
Oui au dernier soutif euh pardon sous-motif on peut passer
(Quoique j'ai déjà vu de jolis sous-motifs sur un soutif ;))

JNP
Merci d'avoir développer ce point ;)
 

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re JM
Pour le 3ème , si tu as compris les 2 1ers, je te propose d'aller + vite , quitte à revenir sur certaines parties si nécessaire.
Il nous reste donc à gérer les autres mois (le cas de février étant réglé) :
"(0[1-9]|[12]\d|3[01])/(01|0[3-9]|1[012])/(190[1-9]|19[1-9]\d|(20|21)\d\d)"

Concernant les jours et les années, le principe étant le même que pour le mois de février (avec l'alternative des jours 30 et 31 en plus), je n'y reviens pas.

Concernant les mois, il nous faut donc éliminer février des alternatives :
(01|0[3-9]|1[012])
Le masque distingue donc 3 cas :
- janvier
- de février à septembre
et les 3 derniers
.

Voila pour le motif.
Si maintenant tu as des questions sur le motif où d'autres points concernant RegExp (des points particuliers concernant la syntaxe, l'utilisation des propriétés (Global, Multiline, IgnoreCase) ou méthodes (Test, Execute, Replace), les objets (MatchCollection, Match), j'essaierai de te répondre dans la mesure de mes possibilités ( et puis de toutes les façons, Jean-Noel:) n'est jamais bien loin).
A+
 

Staple1600

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re


Merci David84, j'espère ne pas être le seul à lire tes explications ;)
(J'adore lire des messages aussi fournis , dommage qu'ils soient si rares)

PS: Je n'ai plus de questions mais une suggestion.
Cela vous intéresserai toi ,JNP, (moioui) , autrui de compléter ce qu'avait JNP en faisant un grand meltingpot des patterns des plus simples au plus extravagants.

On pourrait catégoriser la chose: Dates/Emaills/Adresses/Alphanumériques/Patronymes/ etc...

Qu'en pensez-vous ?
 
Dernière édition:

JNP

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re :),
( et puis de toutes les façons, Jean-Noel:) n'est jamais bien loin).
Via mon clavier, c'est juste à côté :p...

Cela vous intéresserai toi ,JNP, (moioui) , autrui de compléter ce qu'avait JNP en faisant un grand meltingpot des patterns des plus simples au plus extravagants.
...
Qu'en pensez-vous ?
ben que même si j'ai été pas mal loin, je ne suis pas arrivé au bout, alors si on commençait par celui-là :rolleyes:... :
Re :),C'est surtout que le défi était très interressant : des formats de dates et de saisie de date très divers, avec obligation de récupérer la plus petite et la plus grande :p...
Bonne soirée :cool:
 

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re
Cela vous intéresserai toi ,JNP, (moioui) , autrui de compléter ce qu'avait JNP en faisant un grand meltingpot des patterns des plus simples au plus extravagants.

On pourrait catégoriser la chose: Dates/Emaills/Adresses/Alphanumériques/Patronymes/ etc...

Qu'en pensez-vous ?

Que du bien ! On peut toujours voir où cela nous mène...

ben que même si j'ai été pas mal loin, je ne suis pas arrivé au bout, alors si on commençait par celui-là ... :
Envoyé par JNP
Re ,C'est surtout que le défi était très interressant : des formats de dates et de saisie de date très divers, avec obligation de récupérer la plus petite et la plus grande ...

Je trouve le sujet du fichier intéressant mais pas assez "générique".
On pourrait cependant partir de l'idée de traiter les formats de date les plus utilisés (quand je parle de format de date, je ne pense pas au format en lui-même mais à la chaîne de caractère qui s'y réfère).

Plutôt que de faire un motif pour chacun d'entre eux, nous pourrions nous inspirer du traitement qu'utilise Jean-Noël dans le fichier évoqué afin de transformer les différentes chaînes de caractères traitées en chaînes de type jj/mm/aaaa, puis de soumettre ces chaînes au filtre du RegExp (filtre syntaxique), puis de CDate (filtre de validité), ce qui nous donnerait 3 cas possibles :
- chaîne de caractère ramenée sous forme de chaîne jj/mm/aaaa quelle que soit la chaîne de départ
- Date non valide (car format actuellement non traité par le code)
- Date non valide (format traité mais non validé par CDate car la date n'existe pas).

Ci-joint un début de traitement non finalisé, simplement pour voir si le procédé mérite d'être développé et si l'angle d'attaque est le bon (point très important à caler d'entrée sinon on va rapidement s'engager dans la mise au point d'une usine à gaz:confused:).
A+
 

Pièces jointes

  • Test_date_format.xls
    43 KB · Affichages: 62
  • Test_date_format.xls
    43 KB · Affichages: 56
  • Test_date_format.xls
    43 KB · Affichages: 63

david84

XLDnaute Barbatruc
Re : Expressions régulières - Patterns pour RegExp

Re Re
Modification du motif afin de distinguer les mois de 30 jours de ceux de 31 jours (personne n'avait remarqué le bug:eek: ?!).
On peut ainsi se passer du test conditionnel pour la valeur d'erreur puisque ce cas est maintenant géré par le motif.
NB : j'ai laissé pour l'instant les différents Execute dans le code pour permettre la vérification des valeurs renvoyées par les subMatches.
Code:
Function testDate(Cellule As Range) As String
'Application.Volatile
Dim oRexp, Match, Matches, MaDate As Date, MesMois, MesMoisBis, I As Integer, MaChaine As String, Jour As Long, An As Long
If Cellule = "" Then Exit Function
MesMois = Array("janvier", "janv.", "janv", "février", "févr.", "févr", "mars", "avril", "avr.", " avr ", "mai", "juin", _
    "juillet", "juil.", "juil", "août", "septembre", "sept.", "sept", "octobre", "oct.", "oct", "novembre", "nov.", "nov", "décembre", "déc.", "déc")
    
MesMoisBis = Array("/01/", "/01/", "/01/", "/02/", "/02/", "/02/", "/03/", "/04/", "/04/", "/04/", "/05/", "/06/", "/07/", "/07/", "/07/", _
    "/08/", "/09/", "/09/", "/09/", "/10/", "/10/", "/10/", "/11/", "/11/", "/11/", "/12/", "/12/", "/12/")
MaChaine = Cellule.Value

For I = LBound(MesMois) To UBound(MesMois)
    If InStr(1, Cellule.Value, MesMois(I), vbTextCompare) > 0 Then
        MaChaine = Trim(Replace(MaChaine, MesMois(I), MesMoisBis(I)))
        MaChaine = Replace(MaChaine, " /", "/")
        MaChaine = Replace(MaChaine, "/ ", "/")
        Exit For
    End If
Next I

Set oRexp = CreateObject("vbscript.regexp")
With oRexp
    .Global = True
    .Pattern = "(\b\d{1})(/\d{2}/\d{4})"
    Set Matches = .Execute(MaChaine)
    MaChaine = .Replace(MaChaine, "0$1$2")
    .Pattern = "(\b\s)(\d{1})(/\d{2}/)((?:0|1|2)[0-9])"
    Set Matches = .Execute(MaChaine)
    MaChaine = .Replace(MaChaine, "0$2$320$4")
    .Pattern = "(\b\s)(\d{1})(/\d{2}/)((?:3|4|5|6|7|8|9)[0-9])"
    Set Matches = .Execute(MaChaine)
    MaChaine = .Replace(MaChaine, "$10$2$319$4")
End With

With oRexp
      .Pattern = "((0[1-9]|[12]\d|3[01])/(0[13578]|1[02])|(0[1-9]|[12]\d|30)/(0[469]|11))/(190[1-9]|19[1-9]\d|(20|21)\d\d)|(0[1-9]|[1]\d|[2][0-8])/02/(190[1-9]|19[1-9]\d|(20|21)\d\d)|29/02/((190|210)[48]|(19|20|21)([13579][26]|[2468][048])|200[048])"
      .Global = True
      Set Matches = .Execute(MaChaine)
        If .test(MaChaine) = True Then testDate = CDate(Right(MaChaine, 10)) Else testDate = "Date non valide"
End With
End Function
A+
 
Dernière édition:

Statistiques des forums

Discussions
312 612
Messages
2 090 227
Membres
104 453
dernier inscrit
benjiii88