gif animé ordinateur

jeudi 16 décembre 2010

Les expression régulières (regexp), ou comment valider une chaine de caractère en Java

Les expressions rationnelles en Java (ou Regular Expression) comme dans les autres langages ont deux fonctions essentielles: la validation des données et l'analyse textuelle.
Ce modeste article n'a pas pour vocation d'être un cours exhaustif sur les expressions rationnelles mais on ouvrira des portes en décortiquant les exemples les plus utiles. Il ne vous restera plus qu'à les enfoncer.
Commençons par la validation des données pour cet article.
Supposons que je veuille valider qu'une chaîne de caratère est bien un emai
 En java on évoque la méthode statique 'matches' de l'objet Pattern, comme suite 
public static boolean validEmail(String email){        
       //pourrait être amélioré        
       return Pattern.matches(".+@.+\\.[a-z]+",email);        
  }

L'objet "Pattern" dispose d'une méthode statique "matches" permettant de valider rapidement la conformité d'une chaîne à un pattern.
Analysons ce pattern :
Il commence par ".+@" :
"." est une classe de caractère, il signifie n'importe quel caractère.
"+" est un quantificateur, il signifie au moins 1 (1 ou plus),
enfin @ qui n'a aucune signification particulière dans les expressions régulieres ne signifie rien d'autre que @ alias @, @ lui-même, en clair @.
Donc l'ensemble ".+@" siginifie que @ doit être présent, précédé par au moins 1 caractère.
A priori ça a l'air pas mal mais si on creuse un peu on voit tout de suite que c'est pas terrible pour vérifier qu'un mail est valide, quelles sont les chaînes de caractères qui obeissent à ce début de pattern :
mimi@
  mimi@@
  @@mimi@@@@@@@
  _-_-_-56?"""&&&@@@@ (a mon avis c'est pas un bon début pour 
un email valide)
et on peut faire encore plus horrible.
Bon ne desesperons pas, voyons la suite de ce pattern c'est à dire ce qui est situé après l'@.
.+\\.[a-z]+
".+" on connait déjà c'est n'importe quel caractère au moins une fois
ensuite "\\." est déstiné à exprimer "\." mais en java pour exprimer les "\" if faut aussi les echapper : "\." dans une chaine de caractère devient "\\." en java.
Mais alors que signifie "\." finalement ? "\." signifie que l'on veut literralement utiliser un "." dans la chaine de caractère, on ne veut pas que "." soit interprété comme une classe de caractère mais comme un caractère un point c'est tout!! En fait on essaye ici de capturer le "." qui sépare le domaine de son extension 'microsoft.com', 'taghjijt.ma' ...etc.
"[a-z]+" : "[a-z]" est une classe de caractère il signifie tous les caractères allant de a à z, quant à "+" on le connait déjà c'est un quantificateur, il signifie au moins une fois.

Quels sont les chaînes qui correspondent à ce pattern ?
microsoft.com
   microsoft.commerageetautreragot
   micro%%%%%%%%s(((((((oft.z
Là encore c'est pas terrible.

Finalement notre pattern dans sa totalité dit :
n'importe quel caractère mais au moins 1 fois, suivi d'un @ suivi de n'importe quel caractère mais au moins 1 fois suivi d'un '.' suivi de n'importe quel caractère entre a et z mais au moins 1 foi.

L'expérience montre qu'un simple pattern comme celui-là couvre dans la pratique 95% des besoins de validation des emails. Car l'utilisateur après un ou deux refus ne sait pas à quel point le contrôle des emails est fin et préfère gagner du temps en en saisissant un valide.

Mais 5% d'email c'est trop donc je vous propose d'analyser cette nouvelle regexp.
 ^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$

Ce pattern est nettement meilleur et va nous permettre de faire un bien meilleur contrôle des emails. Il est aussi un peu plus complexe.
Commençons son analyse.
D'abord il y a le "^" qui signifie la chaine de caractère doit commencer par ce qui suit le caractère "^". De l'autre coté il y a le $ qui signifie la chaine de caractère doit finir par ce qui précède le caractère "$".
Vient ensuite une classe de caractère "[A-Z0-9._%+-]" ça nous oblige à décrire un peu plus rigoureusement la fontion joué par les crochets "[...]". Quand une chaine de caractère est compris dans des "[...]" cela signifie "ou"
1) [ab] signifie a ou b
2) [abc] signifie a ou b ou c
3) [a-c] signifie n'importe quoi de a à c (identique à la précédente), dans ce cas "-" est un opérateur de plage : la plage de caractère entre "a" et "c"
4) [a-z1-9] sigifie une lettre minuscule ou un chiffre
5) [a-z1-9+] sigifie une lettre minuscule ou un chiffre ou un caractère +, attention entre les crochets les caractères spéciaux perdent leur spécificité et sont interprétés literallement comme des caractères, c'est le cas aussi pour "." Comme on veut aussi proposer le caractère "-" dans la liste des caractères possibles et qu'on ne veut pas que "-" soit interprété comme un opérateur de plage on le met tout à la fin juste avant le crochet fermant.
Donc ^[A-Z0-9._%+-]+@ signifie la chaine de caractère doit commencer par au moins 1 élément de la classe [A-Z0-9._%+-] suivi d'un @
Comparé à l'exemple précédent
mimi@ passe encore mais
mimi@@
   @@mimi@@@@@@@
   _-_-_-56?"""&&&@@@@
ne passe plus, on a progressé !
Poursuivons l'analyse de cette regexp : "[A-Z0-9.-]+\.[A-Z]{2,4}$".
Comme on le sait déjà $ tout à la fin signifie la chaine de caratère doit finir par ce qui précède le $
"[A-Z0-9.-]" veut dire un caratere dans la plage "A-Z" ou "0-9" ou un point "." ou un "-". On peut remarquer que cette classe de caractère est moins large que la classe étudiée précédement en effet il est interdit d'utiliser dans les noms de domaine des caractères comme _ ou %.
Ensuite on connait déjà "\." cela veut dire le caractère "." et non la classe de caractère universelle. Attention dans une chaine de caractère java il faudra l'écrire "\\.".
"[A-Z]{2,4}" veut dire entre 2 et 4 caractères dans la plage A-Z
Mis bout a bout cette deuxieme moitié de regexp nous donne
Il faut que la chaine de caractère se termine par : Au moins un caractère dans la plage "[A-Z0-9.-]" Suivi d'un '.' suivi d'entre deux et quatre caractères dans la plage [A-Z]
Comparé à l'exemple précédent :
microsoft.com passe encore mais
microsoft.commerageetautreragot
   micro%%%%%%%%s(((((((oft.z
ne passe plus.
On est enfin bon, notre regexp valide correctement une adresse mail,
C'est ce genre de regex qui vous refuse parfois la saisie d'une adresse mail non valide sur un site web.

Cette regexp est utilisée en java de la façon suivante :
public static boolean validEmail(String email){
       //beaucoup mieux
       email = email.toUpperCase();
       return Pattern.matches("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$",email);
   }
Bonne lecture, et voici un lien pour plus d'infos sur les regexp. 

Aucun commentaire:

Enregistrer un commentaire