21-06-2008, 02:36 PM
En grand amoureux des regex que je suis, je tente ma chance ... Désolé pour al couleur mais j'ai pas trouvé de meilleur moyen de rendre le tout lisible, en faisant bien al distinction entre texte et regex (la balise php est bien mais elle bouffe mes échappements ...).
Beaucoup de problèmes pour moi (ca veut pas forcément dire que c'est faux, simplement que c'est pas la facon dont moi je les écris, mais je suis incapable de faire la distinction avec certitude):
- Déja tu mets des crochets qui servent à rien. "[;]+" tu peux l'écrire ";" ca marche tout aussi bien (dans notre cas), c'est même plus juste en fait parce que le "+" impliquerait que tu peux avoir plusieurs point virgule de suite alors que dans ce que tu écris c'est pas souhaitable.
- Idem pour le crochet de départ ou pour les "{1}", quand tu veux qu'un signe ne soit pris qu'une seule fois ne met tout simplement pas de caractère de comptage (+ * ? {} ...), ca allège ta regex et rend les erreurs mieux visibles.
- Il y a des raccourcis aussi, comme "+" qui vaut "{1,}", ou "*" qui vaut "{0,}" ou encore "?" qui vaut "{0,1}".
- Donc:
[\[]{0,1}([0-9])+[;]+([0-9])+[;]+([0-9])+[;]+([0-9]){0,1}[;]+([0-9]){0,1}[;]+([0-9]{1}[MPH]{1}[0-9]{1,}){0,}[;]+([0-9,]{0,})+[\]]{0,1}
devient
\[?([0-9])+;([0-9])+;([0-9])+;([0-9])?;([0-9])?;([0-9][MPH][0-9]+)*;([0-9,]*)+\]?
- Ensuite tes parenthèses. Si tu les veux capturantes elles sont mal placées, il faut englober le + après le [0-9] si tu veux capturer tout le nombre. Si tu veux aps capturer elles ne servent à rien, [0-9]+ marche tout aussi bien que ([0-9])+. Corrigeons en gardant l'hypothèse de capture:
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)*;([0-9,]*)+\]?
- On commence à y voir plus clair: "([0-9][MPH][0-9]+)*" est juste mais pas tout à fait exact, c'est plus un "?" qu'un "*" qu'il te faut, ta question ici c'est soit il y a tout ca soit il n'y a rien.
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)?;([0-9,]*)+\]?
- Le dernier tronçon est mal défini, j'imagine qu'il n'existe pas que 10 objets numérotés de 0 à 9 mais que certains objets ont des identifiants à deux chiffres, et ton tronçon ne tiens pas compte de l'ordre des caractères puisque c'est un choix parmi des crochets. Pour être plus précis il faudrait:
((?:[0-9]+,)*) si tu choisis de mettre toujours une virgule à la fin, même quand il n'y a pas d'objet après (un nombre suivi d'une virgule répété X fois, X=0 possible. Le "?:" indique une parenthèse non capturante, si tu veux en plus capturer chaque objet séparement c'est plus compliqué encore, là tu capturera la liste).
((?:[0-9]+,)*[0-9]*) si tu choisis d'utiliser des virgules seulement entre les identifiants. Plus propre mais plus lourd.
- Attardons nous sur les 4 premières coordonnées. TU dis qu'elles sont pareilles, pourtant tu nous écris ca:
([0-9]+);([0-9]+);([0-9]+);([0-9]?);
Moi ce que je vois c'est que les 3 premières sont pareilles, mais que la quatrième c'est un chiffre qui peut exister ou non. On peut simplifier tout ca en:
(?:([0-9]+);){4}
4 fois un nombre suivi d'un point virgule à capturer, le "?:" indique une parenthèse non capturante qui sert simplement à définir ce qui doit apparaitre 4 fois.
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
devient
\[?(?:([0-9]+);){4}([0-9]?);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
("?:" pour ne pas capturer toujours)
- Ton 5eme morceau est sensé être un ID ou rien, là ce que je vois c'est "un chiffre ou rien" puisqu'on a un "?". Un "*" serait plus adapté, il permettrait qu'il y ait plusieurs chiffres ou rien. Donc:
\[?(?:([0-9]+);){4}([0-9]*);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
- Encore un petit détail, il ne faut pas oublier le "^" en dévut de regex et le "$" en fin, sinon il cherchera cette regex dans ta chaine, mais rien ne te garantira que ce ne soit pas qu'une sous-chaine qu'il trouvera. "^" indique que le premier caractère de la regex doit correspondre au premier caractère de la chaine, et "$" que le dernier de la regex correspond au dernier de la chaine.
- Autre détail qui tue qui vient de me sauter à la figure en testant mon résultat: gare aux échappements de crochets ! Généralement il faut double échapper pour que ca fonctionne (en tout cas dans l'exemple qui av suivre).
Donc au final:
^\\[?(?:([0-9]+);){4}([0-9]*);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\\]?$
Une chaine plus courte et plus précise.
Maintenant qu'on y est, l'instant de vérité: est ce que ca marche (quand t'as passé une demi heure à la construire ta regex c'est le moment ou tu te met à trembler :p). Un petit script facile pour vérifier tout ca:
Citation :[\[]{0,1}([0-9])+[;]+([0-9])+[;]+([0-9])+[;]+([0-9]){0,1}[;]+([0-9]){0,1}[;]+([0-9]{1}[MPH]{1}[0-9]{1,}){0,}[;]+([0-9,]{0,})+[\]]{0,1}
Beaucoup de problèmes pour moi (ca veut pas forcément dire que c'est faux, simplement que c'est pas la facon dont moi je les écris, mais je suis incapable de faire la distinction avec certitude):
- Déja tu mets des crochets qui servent à rien. "[;]+" tu peux l'écrire ";" ca marche tout aussi bien (dans notre cas), c'est même plus juste en fait parce que le "+" impliquerait que tu peux avoir plusieurs point virgule de suite alors que dans ce que tu écris c'est pas souhaitable.
- Idem pour le crochet de départ ou pour les "{1}", quand tu veux qu'un signe ne soit pris qu'une seule fois ne met tout simplement pas de caractère de comptage (+ * ? {} ...), ca allège ta regex et rend les erreurs mieux visibles.
- Il y a des raccourcis aussi, comme "+" qui vaut "{1,}", ou "*" qui vaut "{0,}" ou encore "?" qui vaut "{0,1}".
- Donc:
[\[]{0,1}([0-9])+[;]+([0-9])+[;]+([0-9])+[;]+([0-9]){0,1}[;]+([0-9]){0,1}[;]+([0-9]{1}[MPH]{1}[0-9]{1,}){0,}[;]+([0-9,]{0,})+[\]]{0,1}
devient
\[?([0-9])+;([0-9])+;([0-9])+;([0-9])?;([0-9])?;([0-9][MPH][0-9]+)*;([0-9,]*)+\]?
- Ensuite tes parenthèses. Si tu les veux capturantes elles sont mal placées, il faut englober le + après le [0-9] si tu veux capturer tout le nombre. Si tu veux aps capturer elles ne servent à rien, [0-9]+ marche tout aussi bien que ([0-9])+. Corrigeons en gardant l'hypothèse de capture:
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)*;([0-9,]*)+\]?
- On commence à y voir plus clair: "([0-9][MPH][0-9]+)*" est juste mais pas tout à fait exact, c'est plus un "?" qu'un "*" qu'il te faut, ta question ici c'est soit il y a tout ca soit il n'y a rien.
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)?;([0-9,]*)+\]?
- Le dernier tronçon est mal défini, j'imagine qu'il n'existe pas que 10 objets numérotés de 0 à 9 mais que certains objets ont des identifiants à deux chiffres, et ton tronçon ne tiens pas compte de l'ordre des caractères puisque c'est un choix parmi des crochets. Pour être plus précis il faudrait:
((?:[0-9]+,)*) si tu choisis de mettre toujours une virgule à la fin, même quand il n'y a pas d'objet après (un nombre suivi d'une virgule répété X fois, X=0 possible. Le "?:" indique une parenthèse non capturante, si tu veux en plus capturer chaque objet séparement c'est plus compliqué encore, là tu capturera la liste).
((?:[0-9]+,)*[0-9]*) si tu choisis d'utiliser des virgules seulement entre les identifiants. Plus propre mais plus lourd.
- Attardons nous sur les 4 premières coordonnées. TU dis qu'elles sont pareilles, pourtant tu nous écris ca:
([0-9]+);([0-9]+);([0-9]+);([0-9]?);
Moi ce que je vois c'est que les 3 premières sont pareilles, mais que la quatrième c'est un chiffre qui peut exister ou non. On peut simplifier tout ca en:
(?:([0-9]+);){4}
4 fois un nombre suivi d'un point virgule à capturer, le "?:" indique une parenthèse non capturante qui sert simplement à définir ce qui doit apparaitre 4 fois.
\[?([0-9]+);([0-9]+);([0-9]+);([0-9]?);([0-9]?);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
devient
\[?(?:([0-9]+);){4}([0-9]?);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
("?:" pour ne pas capturer toujours)
- Ton 5eme morceau est sensé être un ID ou rien, là ce que je vois c'est "un chiffre ou rien" puisqu'on a un "?". Un "*" serait plus adapté, il permettrait qu'il y ait plusieurs chiffres ou rien. Donc:
\[?(?:([0-9]+);){4}([0-9]*);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\]?
- Encore un petit détail, il ne faut pas oublier le "^" en dévut de regex et le "$" en fin, sinon il cherchera cette regex dans ta chaine, mais rien ne te garantira que ce ne soit pas qu'une sous-chaine qu'il trouvera. "^" indique que le premier caractère de la regex doit correspondre au premier caractère de la chaine, et "$" que le dernier de la regex correspond au dernier de la chaine.
- Autre détail qui tue qui vient de me sauter à la figure en testant mon résultat: gare aux échappements de crochets ! Généralement il faut double échapper pour que ca fonctionne (en tout cas dans l'exemple qui av suivre).
Donc au final:
^\\[?(?:([0-9]+);){4}([0-9]*);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\\]?$
Une chaine plus courte et plus précise.
Maintenant qu'on y est, l'instant de vérité: est ce que ca marche (quand t'as passé une demi heure à la construire ta regex c'est le moment ou tu te met à trembler :p). Un petit script facile pour vérifier tout ca:
Code PHP :
<?php
<html>
<head>
<script>
function pwet()
{
var regex = new RegExp("^\\[?(?:([0-9]+);){4}([0-9]*);([0-9][MPH][0-9]+)?;((?:[0-9]+,)*[0-9]*)\\]?$", "g");
var Chaine1 = "[48;39;1;0;;;";
var Chaine2 = "48;39;1;0;;;";
var Chaine3 = "48;39;1;0;;;]";
var Chaine4 = "48;39;1;0;;0M15;";
var Chaine5 = "48;39;1;0;;18;";
var Chaine6 = "48;39;1;0;14;1H54122;8"
if (Chaine1.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
if (Chaine2.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
if (Chaine3.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
if (Chaine4.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
if (Chaine5.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
if (Chaine6.match(regex)) { document.writeln("TRUE<br>"); } else { document.writeln("FALSE<br>"); }
}
</script>
</head>
<body onload="pwet()">
</body>
</html>
Verdict:
Citation :TRUE
TRUE
TRUE
TRUE
FALSE
TRUE
Horreur, une erreur. Le soucis c'est que l'erreur ne vient pas de ma regex adorée mais de ton énoncé: qu'est ce que c'est que ce "18" en avant dernier tronçon de la chaine 5 ?! Ca ne correspond pas à ce que tu nous a décrit ...