Aller au contenu

Les expressions régulières


(gile)

Messages recommandés

Salut

 

Il en avait été question (sans succès) dans ce sujet, et ces dernier temps j'ai eu à m'en servir ici en LISP et en C#.

 

Les expressions régulières permettent de rechercher les correspondances avec un motif dans une chaîne de caractère, un peu comme wcmatch mais avec la possibilité de récupérer ou de remplacer ces correspondances.

Les caractères utilisés pour la définition du motif (ou modèle, pattern en anglais) sont beaucoup plus nombreux que les caractères génériques utilisés avec wcmatch, ce qui fait des expression régulières un mécanisme beaucoup plus puissant, mais aussi plus obscur quant à la création des motifs (voir ce tableau récapitulatif).

 

L'objet RegExp de VBScript, accessible en LISP avec ActiveX, expose trois propriétés et trois méthodes que j'ai pensé utile d'implémenter en LISP à travers quatre fonctions.

 

;;--------------------------------------------------------------------------------------------------------;;
;;                                         Expressions Régulières                                         ;;
;;                                                                                                        ;;
;; Référence                                                                                              ;;
;; http://tahe.developpez.com/tutoriels-cours/vbscript/?page=page_4#LIV-B                                 ;;
;; https://msdn.microsoft.com/en-us/library/1400241x(VS.85).aspx                                          ;;
;;--------------------------------------------------------------------------------------------------------;;

(vl-load-com)

;; RegExpSet
;; Retourne l'instance de VBScript.RegExp courante après avoir défini ses propriétés.
;;
;; Arguments
;; pattern    : Motif à rechercher.
;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse.
;; global     : Si non nil, recherche toutes les occurrences du modèle ;
;;              si nil, recherche uniquement la première occurrence.

(defun RegExpSet (pattern ignoreCase global / regex)
 (setq	regex
 (cond
   ((vl-bb-ref '*regexp*))
   ((vl-bb-set '*regexp* (vlax-create-object "VBScript.RegExp")))
 )
 )
 (vlax-put regex 'Pattern pattern)
 (if ignoreCase
   (vlax-put regex 'IgnoreCase acTrue)
   (vlax-put regex 'IgnoreCase acFalse)
 )
 (if global
   (vlax-put regex 'Global acTrue)
   (vlax-put regex 'Global acFalse)
 )
 regex
)

;; RegexpTest
;; Retourne T si une correspondance avec le motif a été trouvée dans la chaîne ; sinon, nil.
;;
;; Arguments
;; string     : Chaîne dans la quelle on recherche le motif.
;; pattern    : Motif à rechercher.
;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse.
;;
;; Exemples :
;; (RegexpTest "foo bar" "Ba" nil)  ; => nil
;; (RegexpTest "foo bar" "Ba" T)    ; => T
;; (RegExpTest "42C" "[0-9]+" nil)  ; => T

(defun RegexpTest (string pattern ignoreCase)
 (= (vlax-invoke (RegExpSet pattern ignoreCase nil) 'Test string) -1)
)

;; RegExpExecute
;; Retourne la liste des correspondances avec le motif trouvées dans la chaine.
;; Chaque correspondance est renvoyée sous la forme d'une sous-liste contenant :
;; - la valeur de la correspondance,
;; - l'index du premier caractère (base 0)
;; - une liste des sous groupes.
;;
;; Arguments
;; string     : Chaîne dans la quelle on recherche le motif.
;; pattern    : Motif à rechercher.
;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse.
;; global     : Si non nil, recherche toutes les occurrences du modèle ;
;;              si nil, recherche uniquement la première occurrence.
;;
;; Exemples
;; (RegExpExecute "foo bar baz" "ba" nil nil)               ; => (("ba" 4 nil))
;; (RegexpExecute "12B 4bis" "([0-9]+)([A-Z]+)" T T)        ; => (("12B" 0 ("12" "B")) ("4bis" 4 ("4" "bis")))
;; (RegexpExecute "-12 25.4" "(-?\\d+(?:\\.\\d+)?)" nil T)  ; => (("-12" 0 ("-12")) ("25.4" 4 ("25.4")))

(defun RegExpExecute (string pattern ignoreCase global / sublst lst)
 (vlax-for match (vlax-invoke (RegExpSet pattern ignoreCase global) 'Execute string)
   (setq sublst nil)
   (vl-catch-all-apply
     '(lambda ()
 (vlax-for submatch (vlax-get match 'SubMatches)
   (if submatch
     (setq sublst (cons submatch sublst))
   )
 )
      )
   )
   (setq lst (cons (list (vlax-get match 'Value)
		  (vlax-get match 'FirstIndex)
		  (reverse sublst)
	    )
	    lst
      )
   )
 )
 (reverse lst)
)

;; RegExpReplace
;; Retourne la chaîne après remplacement des correspondances avec le motif.
;;
;; Arguments
;; string     : Chaîne dans la quelle on recherche le motif.
;; pattern    : Motif à rechercher.
;; newStr     : Chaîne de remplacement.
;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse.
;; global     : Si non nil, recherche toutes les occurrences du modèle ;
;;              si nil, recherche uniquement la première occurrence.
;;
;; Exemples :
;; (RegexpReplace "foo bar baz" "a" "oo" nil T)                  ; => "foo boor booz"
;; (RegexpReplace "foo bar baz" "(\\w)\\w(\\w)" "$1U$2" nil T)   ; => "fUo bUr bUz"
;; (RegexpReplace "$ 3.25" "\\$ (\\d+(\\.\\d+)?)" "$1 €" nil T)  ; => "3.25 €"

(defun RegExpReplace (string pattern newStr ignoreCase global)
 (vlax-invoke (RegExpSet pattern ignoreCase global) 'Replace string newStr)
)

  • Upvote 1

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Très intéressant ces expressions régulières.

Tes fonctions pour appliquer le script m'ont l'air, comme d'habitude, impeccables.

Bien vu dans un récent message la très grande praticité pour l'édition des Mtexts.

Super bonne idée ;)

J'ai donc regardé le tableau récapitulatif que tu indiques, cela me rafraichit la mémoire car en effet d'aucuns d'entre vous en ont déjà discutés et je l'avais déjà vu.

Si j'ai bien compris il n'y a que ( et \ qui soient des caractères spéciaux à faire précéder d'un \ pour fonctionner tel.

 

Mais ne pourrais-tu pas indiquer ce lien ou un autre d'un wiki dans tes commentaires, ca serait une bonne idée de voir ce tableau rapidement pour qui utilise cette biblio. D'ailleurs face à celui de wcmatch que tu peux indiquer aussi en lien, après tout, qu'en penses-tu ?

Bureau d'études dessin.

Spécialiste Escaliers

Développement - Formation

 

./__\.
(.°=°.)
Lien vers le commentaire
Partager sur d’autres sites

Quelques exemples.

 

RegExpTest est un peu l'équivalent de wcmatch mais permet une recherche plus fine.

Par exemple, pour savoir si une chaîne représente un nombre entier positif, autrement dit si elle est constituée uniquement de un ou plusieurs chiffres.

(RegExpTest "42" "^\\d+$" T)

"^\\d+$" : 
^     début de la chaîne 
\\d+  un ou plusieurs chiffres
$     fin de la chaîne

Ou si la chaîne représente un nombre (entier ou réel positif ou négatif)

(RegExpTest "-2.5" "^-?\\d+(\\.\\d+)?$" T)

"^-?\\d+(\\.\\d+)?$"
^           début de la chaîne
-?          zéro ou une fois le caractère '-'
\\d+        un ou plusieurs chiffres
(\\.\\d+)?  zéro ou une fois un point suivi de un ou plusieurs chiffres
$           fin de la chaîne

 

RegExpExecute permet de récupérer le résultat complet d'une recherche et de constituer des groupements de correspondances.

(RegExpExecute "B12" "([A-Z]+)(\\d+)" T nil)

"([A-Z]+)(\\d+)"
([A-Z]+)  groupe de une ou plusieurs lettres
(\\d+)    groupe de un ou plusieurs chiffres

Cette expression retourne : (("B12" 0 ("B" "12"))) et on peut facilement récupérer d'un côté la valeur littérale (colonne d'une cellule) et de l'autre la valeur numérique (rangée d'une cellule).

 

 

RegExpReplace permet de remplacer plusieurs correspondances (avec global /= nil) en utilisant des groupes dans la chaîne de remplacement.

Soit une chaîne représentant le texte d'un texte multiligne contenant des formatages :

(setq str "{\\C1;foo} {\\L\\C30;bar}")

Supprimer uniquement les formatages de couleur :

(setq str (RegExpReplace str "\\\\C\\d+;" "" T T))

"\\\\C\\d+;"
\\\\C  le caractère '\\C'
\\d+   un ou plusieurs chiffres
;      le caractère ';'

Cette expression retourne : "{foo} {\\Lbar}"

Supprimer les accolades uniquement si elles ne contiennent pas de caractère '\\' :

(setq str (RegExpReplace str "{([^\{\\\\]+)}" "$1" nil T))

Le motif crée des groupes avec la partie de la chaîne qui se trouve entre accolades et ne contient pas de caractère '\\'.

"{([^\{\\\\]+)}"
{     caractère '{'
(     début d'un groupement
[^    ne contient pas les caractères
\{    accolade ouvrante '{'
\\\\  double anti-slash '\\'
]     fin des caractères exclus
+     une fois ou plus
)     fin de groupement
}     caractère '}'

Cette expression retourne : "foo {\\Lbar}"

 

Petite explication.

Avec RegexpExecute :

(RegExpExecute "{foo} {\\Lbar}" "{([^\{\\\\]+)}" nil T)

retourne : (("{foo}" 0 ("foo")))

On voit une occurrence trouvée "{foo}" à l'index 0 et une liste des groupements ("foo").

Le symbole "$1" utilisé comme chaîne de remplacement correspond au premier groupe dans cette liste, soit "foo" ; l'occurrence "{foo}" est remplacée par "foo".

 

De cette dernière, on peut tirer une petite routine utile :

 

(defun StripMtextColor (mtext / str)
 (setq str (getpropertyvalue mtext "Contents"))
 (setq str (RegexpReplace str "\\\\C\\d+;" "" T T))
 (setq str (RegexpReplace str "{([^\\\\\{]+)}" "$1" T T))
 (setpropertyvalue mtext "Contents" str)
)

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Si j'ai bien compris il n'y a que ( et \ qui soient des caractères spéciaux à faire précéder d'un \ pour fonctionner tel.

Non, c'est valable pour tous les caractères spéciaux : . $ { [ ( | ) * + \

 

Mais ne pourrais-tu pas indiquer ce lien ou un autre d'un wiki dans tes commentaires, ca serait une bonne idée de voir ce tableau rapidement pour qui utilise cette biblio

C'est fait, j'aurais aimé trouver mieux en français (comme celui-là, mais il concerne les expressions régulières .NET qui offrent encore plus de fonctionnalités.)

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

. $ { [ ( | ) * + \

noté.

 

Grand Dieu ! Pas le temps de lire tout cela aujourd'hui si je veux faire posément.

Déjà (Je n'ai pas encore chargé de routine et essaie d'appréhender) :

(RegexpReplace "foo bar baz" "(\\w)\\w(\\w)" "$1U$2" nil T)

  • Se comprend pour remplacer une lettre avec le modèle (modèle) mais pourquoi double \ ?
  • Et pourquoi $1U$2, j'aurais pensé $0U$1 car la doc parle de l'item 0...

Bureau d'études dessin.

Spécialiste Escaliers

Développement - Formation

 

./__\.
(.°=°.)
Lien vers le commentaire
Partager sur d’autres sites

. $ { [ ( | ) * + \

noté.

 

Grand Dieu ! Pas le temps de lire tout cela aujourd'hui si je veux faire posément.

Déjà (Je n'ai pas encore chargé de routine et essaie d'appréhender) :

(RegexpReplace "foo bar baz" "(\\w)\\w(\\w)" "$1U$2" nil T)

  • Se comprend pour remplacer une lettre avec le modèle (modèle) mais pourquoi double \ ?
  • Et pourquoi $1U$2, j'aurais pensé $0U$1 car la doc parle de l'item 0...

 

1) En VB(Script) le modèle serait : "(\w)\w(\w)", mais en LISP (comme dans tous les langages où le caractère '\' est le caractère d'échappement), il faut le doubler.

2) En fait, à l'index 0, c'est la correspondance complète du Match (en tout cas, c'est comme ça en .NET avec Match.Groups), donc pour les SubMatches, on commence à 1.

 

Edit : pour les symboles $1, $2, etc. il semble que ce soit simplement une convention, $1 correspond au premier 'sub-match' (voir le chapitre Phone number back reference)

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité