2009-08-20 5 views
5

Je construis une API javascript SCORM 2004 pour un système de gestion de l'apprentissage, et l'une des exigences de SCORM 2004 est que les intervalles de temps qui y sont passés doivent suivre le format suivant. Est-ce que quelqu'un sait ce que l'expression régulière de ce serait? J'essaie d'envelopper mon esprit, mais en vain. Note: P doit toujours être le premier caractère.SCORM 2004 Format de l'heure - Expression régulière?

P [yy] [mM] [dD] [T [hH] [nM] [S [S] .s]] où:

  • y: Le nombre d'années (entier ,> = 0, et non restreint)
  • m: le nombre de mois (entier,> = 0, et non restreint)
  • d: Le nombre de jours (entier,> = 0, ne se limite pas)
  • h : Le nombre d'heures (entier,> = 0, non restreint)
  • n: Le nu mbre de minutes (entier,> = 0, non restreint)
  • s: Le nombre de secondes ou fraction de secondes (réel ou entier,> = 0, pas restreint). Si des fractions de seconde sont utilisées, SCORM restreint davantage la chaîne à un maximum de 2 chiffres (par exemple, 34,45 - valide, 34,45454545 - non valide).
  • Les indicateurs littéraux de caractère P, Y, M, D, T, H, M et S doivent apparaître si la valeur non nulle correspondante est présente.
  • Le remplissage nul des valeurs doit être pris en charge. Zéro-remplissage ne modifie pas la valeur entière du nombre étant représenté par un ensemble de caractères. Pour l'exemple , PT05H est équivalent à PT5H et PT000005H.

Exemple -

  • P1Y3M2DT3H indique une période de 1 an, 3 mois, 2 jours et 3 heures
  • PT3H5M indique une période de temps de 3 heures et 5 minutes

Toute aide serait grandement appréciée.

Merci!

MISE À JOUR:

J'ai ajouté quelques normes supplémentaires qui doivent être conservés -

  • Le désignateur P est présente
  • Si la valeur des années, mois, jours, heures, minutes ou secondes est zéro, la valeur et la désignation littérale correspondante peuvent être omis, mais au moins un caractère r désignateur littéral et valeur sont présents en plus du désignateur P
  • Le T désignateur est omise si tous les composants de temps (heures, minutes et secondes ) ne sont pas utilisés.Une valeur nulle peut être utilisé avec un quelconque des composants de temps (par exemple, PT0S)

Répondre

5

Voici la regex que j'utilise;

^P(?=\w*\d)(?:\d+Y|Y)?(?:\d+M|M)?(?:\d+D|D)?(?:T(?:\d+H|H)?(?:\d+M|M)?(?:\d+(?:\­.\d{1,2})?S|S)?)?$ 
+0

J'adapté cela pour ColdFusion, et je l'ai trouvé ce que je pense est une erreur (au moins en CF) pour la partie "secondes". Je pense que la partie secondes devrait être (\ d + (?: \. \ D {1,2})? S | S)? – hairbo

0

Utilisez [0-9] pour correspondre à tout chiffre. + pour correspondre à une ou plusieurs répétitions. ? pour correspondre à 0 ou 1 répétitions. () pour regrouper et extraire la sortie.

P(([0-9]+Y)?([0-9]+M)?([0-9]+D)?)(T([0-9]+H)?([0-9]+M)?([0-9.]+S)?)?

import re 

>>> p = re.compile('P(([0-9]+Y)?([0-9]+M)?([0-9]+D)?)(T([0-9]+H)?([0-9]+M)?([0-9.]+S)?)?') 

>>> p.match('P1Y3M2DT3H').groups() 
('1Y3M2D', '1Y', '3M', '2D', 'T3H', '3H', None, None) 

>>> p.match('P3M2DT3H').groups() 
('3M2D', None, '3M', '2D', 'T3H', '3H', None, None) 

>>> p.match('PT3H5M').groups() 
('', None, None, None, 'T3H5M', '3H', '5M', None) 

>>> p.match('P1Y3M4D').groups() 
('1Y3M4D', '1Y', '3M', '4D', None, None, None, None) 
+0

Cette regex est trop permissive pour les secondes et correspondre aux valeurs comme ' '1.1.1.1.1''. – FMc

+0

Cette expression fonctionne à l'exception du détail commenté par FM. Je l'ai modifié un peu pour permettre à 0 d'être à la place de 0M par exemple (selon les normes), donc j'ai maintenant: P ((([0-9] + Y) | 0)? (([0 -9] + M) | 0)? (([0-9] + D) | 0)?) (T (([0-9] + H) | 0)? (([0-9] + M) | 0)? (([0-9.] + S) | 0)?) J'ai également trouvé d'autres exigences et j'ai mis à jour la question originale. –

+0

"0 à la place de 0M"? Où est-ce dans la spécification? –

1

JavaScript ne prend pas en charge /x (mode sans espacement ou commentaires), donc supprimer l'espace de cette regex avant de l'utiliser.

/^P(?=.) 
(?:\d+Y)? 
(?:\d+M)? 
(?:\d+D)? 
(?:T(?=.) 
    (?:\d+H)? 
    (?:\d+M)? 
    (?:\d+ 
     (?:\.\d{1,2})? 
    )? 
)?$/i 

Chaque (?=.) affirme qu'il y préanalyse ya au moins un caractère restant à ce moment-là dans le match. Cela signifie qu'au moins un des groupes suivants (c'est-à-dire, le groupe Y, M, D ou T après le P, et le groupe H, M ou S après le T) doit correspondre, même s'ils sont tous facultatifs. Cela satisfait la deuxième des exigences ajoutées dans votre spécification mise à jour.

+0

Merci beaucoup pour votre regex, mais j'ai trouvé quelques exemples qui cassent. Je crois que chacun de ce qui suit est valide dans la spécification et devrait retourner vrai mais retourner faux (excuse si je me trompe!): PT00S (la partie entière de date peut être omise par la spec, et les valeurs de zéro sont acceptables pour H/M/Ss) PYMDTH23M15S (lettres peuvent être présents w/o nombre) PYMDT2HM15.23S (lettres peuvent être présentes w/o chiffres) – pipwerks

0

Notre implémentation de moteur SCORM utilise une combinaison d'une expression régulière similaire à celles ci-dessus et une certaine logique JavaScript de base effectue une validation supplémentaire.

1

Peut-être est la sémantique, mais cette partie de la spécification SCORM peut être interprété comme signifiant littéraux sont autorisés même si une valeur est non fournie:

Le caractère littéraux désignateurs P, Y, M, D , T, H, M et S doivent apparaître si la valeur non nulle correspondante est présente.

"doit apparaître", ce qui signifie qu'un littéral DOIT être présent si le nombre correspondant est présent; il ne dit pas "apparaîtra UNIQUEMENT" si le numéro correspondant est présent.

J'ai modifié regex d'Alan pour gérer cette possibilité (merci, Alan):

^P(?:\d+Y|Y)?(?:\d+M|M)?(?:\d+D|D)?(?:T(?:\d+H|H)?(?:\d+M|M)?(?:\d+(?:\.\d{1,2})?S|S)?)?$ 

Le seul bug que j'ai trouvé est jusqu'à présent un échec pour marquer une chaîne qui n'a pas des valeurs numériques spécifiées, telles que "PTS". Le minimum en fonction de la spécification est « P » suivi d'une valeur unique et la désignation d'accompagnement, tels que P1Y (= 1 an) ou PT0S (= 1 seconde):

au moins un caractère littéral désignateur et de la valeur doivent être présents en plus du désignateur P

Il doit y avoir un moyen d'ajouter un chèque d'une valeur numérique à cette regex, mais mon regex-fu est pas si forte. :)

0

J'utilise cette expression:

^P(\d+Y)?(\d+M)?(\d+D)?(T(((\d+H)(\d+M)?(\d+(\.\d{1,2})?S)?)|((\d+M)(\d+(\.\d{1,2})?S)?)|((\d+(\.\d{1,2})?S))))?$ 

Cette expression ne correspond pas à une valeur comme « PYMDT0H »: un chiffre doit accompagner le désignateur à apparier.

0

Pour ce que ça vaut, j'ai adapté la réponse acceptée pour une utilisation avec Cold Fusion. Je pensais que certaines personnes pourraient trouver cela utile, alors j'ai pensé que je l'afficherais. Comme indiqué ci-dessus, les FC ont bombardé l'implémentation des secondes ci-dessus, donc je l'ai modifié. Je ne suis pas sûr si cela signifie que c'est une erreur générale de RegEx dans l'exemple ci-dessus, ou si CF et JS ont différentes implémentations de RegEx. Quoi qu'il en soit, voici les FC RegEx, avec des commentaires (parce que, vous savez, les expressions autrement régulières sont du charabia complet):

<cfset regex = "(?x) ## allow for multi-line expression, including comments (oh, glorious comments) 
      ^## ensure that this expression occurs at the start of the string 
      P ## the first character must be a P 
      (\d+Y|Y)? ## year (the ? indicates 0 or 1 times) 
      (\d+M|M)? ## month 
      (\d+D|D)? ## day 
      (?:T ## T delineates between day and time information 
      (\d+H|H)? ## hour 
      (\d+M|M)? ## minute 
      (\d+(?:\.\d{1,2})?S|S)? ## seconds and milliseconds. The inner ?: ensure that the sub-sub-expression isn't returned as a separate thing 
      )? ## closes 'T' subexpression 
      $ ## ensure that this expression occurs at the end of the string. In conjunction with leading ^, this ensures that the string has no extraenous characters"> 

Après cela, vous exécutez contre votre chaîne comme ceci:

<cfset result = reFind(regex,mystring,1,true)> 

Cela renvoie un tableau de sous-expressions, que vous pouvez parcourir pour obtenir les pièces discrètes:

<cfloop from=1 to=#arrayLen(result.len)# index=i> 
    <cfif result.len[i] GT 0> 
    #mid(mystring, result.pos[i], result.len[i])#<br> 
    </cfif> 
</cfloop>