2008-11-06 5 views
9

Ok, donc j'ai ce regex:Puis-je optimiser ce phone-regex?

(|^|>)(((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})))(|$|<) 

Il formate les numéros de téléphone néerlandais et belges (je veux seulement ceux où le 31 et 32 ​​comme le code du pays).

Ce n'est pas très amusant à déchiffrer, mais comme vous pouvez le voir, il a également beaucoup dupliqué. mais maintenant il ne les manipule de façon très précise

Tous les numéros de téléphone au format européennes suivantes sont acceptées

00312
0031223234567 
0031612345678 
+31(0)20-1234567 
+31(0)223-234567 
+31(0)6-12345678 
020-1234567 
0223-234567 
06-12345678 
02
0223234567 
0612345678 

et ceux formatés faux suivants ne sont pas

06-1234567 (mobile phone number in the Netherlands should have 8 numbers after 06) 
0223-1234567 (area code with home phone) 

par opposition à ce qui est bon .

020-1234567 (area code with 3 numbers has 7 numbers for the phone as opposed to a 4 number area code which can only have 6 numbers for phone number) 

Comme vous pouvez le voir est le caractère « - » qui le rend un peu difficile mais je besoin là-dedans parce qu'il est une partie de la mise en forme habituellement utilisée par les gens, et je veux être en mesure de les analyser tout.

Maintenant, est ma question ... voyez-vous un moyen de simplifier cette regex (ou même l'améliorer si vous voyez une faute en elle), tout en gardant les mêmes règles?

Vous pouvez le tester à regextester.com

(Le « (|^|>) » est de vérifier si elle est au début d'un mot avec la possibilité qu'il soit précédé soit par une nouvelle ligne ou un ' > 'Je recherche les numéros de téléphone dans les pages HTML.)

+0

Ma première question est: avez-vous vraiment besoin de TOUTES ces captures? Ne pouvez-vous pas simplement saisir les parties importantes et reformater. Quelles sont les parties pertinentes? – Axeman

+0

non je recherche les phonenumbers dans un tas de textes je ne sais pas où le nombre est et de quelle manière conventionnelle son formaté. après que je l'ai trouvé je n'ai fondamentalement plus besoin de lui – youri

Répondre

12

Première observation: la lecture de la regex est un cauchemar. Il pleure pour le mode Perl/x.

Deuxième observation: il y a beaucoup, beaucoup et beaucoup de parenthèses capturantes dans l'expression (42 si je compte correctement, et 42 est, bien sûr, "La réponse à la vie, l'univers et tout" - voir Douglas Adams "Guide de la galaxie d'Hitchiker" si vous avez besoin de cela expliqué).

Bill le Lézard note que vous utilisez plusieurs fois '(-)?()?'. Il n'y a pas d'avantage évident comparé à '-? ?' ou '[- ]?', à moins que vous n'ayez vraiment l'intention de capturer la ponctuation réelle séparément (mais il y a tellement de parenthèses de capture qui fonctionnent que les éléments '$ n' à utiliser seraient difficile).

Donc, nous allons essayer éditer une copie de votre one-liner:

(|^|>) 
(
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

OK - maintenant, nous pouvons voir la structure régulière de votre expression régulière.

Il y a beaucoup plus d'analyses possibles à partir d'ici. Oui, il peut y avoir de grandes améliorations à l'expression régulière. La première, évidente, consiste à extraire la partie du préfixe international et à l'appliquer une fois (optionnellement, ou exiger le zéro initial), puis à appliquer les règles nationales.

(|^|>) 
(
    (((\+|00)(31|32)()?(\(0\))?)|0) 
    (((([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    (((([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    (((([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

Ensuite, nous pouvons simplifier la ponctuation comme indiqué précédemment, et de supprimer quelques parenthèses plausiblement redondantes et améliorer le code du pays reconnaisseur:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) 
    (((([0-9]{2})-? ?)?)[0-9]{7}) | 
    (((([0-9]{3})-? ?)?)[0-9]{6}) | 
    (((([0-9]{1})-? ?)?)[0-9]{8}) 
) 
(|$|<) 

Nous pouvons observer que l'expression régulière ne fait pas respecter les règles relatives à les codes de téléphone portable (il n'insiste donc pas sur le fait que '06' est suivi de 8 chiffres, par exemple). Il semble aussi permettre que le code 'exchange' à 1, 2 ou 3 chiffres soit facultatif, même avec un préfixe international - probablement pas ce que vous aviez en tête, et corrigeant cela supprime encore plus de parenthèses. Nous pouvons supprimer entre parenthèses encore plus après, conduisant à:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) # International prefix or leading zero 
    ([0-9]{2}-? ?[0-9]{7}) |  # xx-xxxxxxx 
    ([0-9]{3}-? ?[0-9]{6}) |  # xxx-xxxxxx 
    ([0-9]{1}-? ?[0-9]{8})   # x-xxxxxxxx 
) 
(|$|<) 

Et vous pouvez travailler plus optimisations d'ici, je l'espère.

+1

merci je l'ai cassé pour moi-même pour voir si je pourrais réaliser ceci mais j'ai dû faire quelque chose de mal ... merci c'est vraiment utile – youri

+1

très vieille bosse mais j'ai juste vu la partie au sujet de 42 ... thats agréable: P acclamations mate: P – youri

+0

comment pouvez-vous obtenir ce travail avec PHP et preg_replace? – Sanne

8

Bon Dieu Tout Puissant, quel gâchis! :) Si vous avez des règles sémantiques ou commerciales de haut niveau (comme celles que vous décrivez en parlant de chiffres européens, de nombres aux Pays-Bas, etc.), vous feriez mieux de transposer ce simple test d'expression rationnelle en plusieurs tests d'expression rationnelle. un pour chacune de vos règles de haut niveau.

if number =~ /...../ # Dutch mobiles 
    # ... 
elsif number =~ /..../ # Belgian landlines 
    # ... 
# etc. 
end 

Ce sera un peu plus facile à lire, à maintenir et à modifier de cette façon.

+0

Et commandez vos tests par le plus susceptible de correspondre (en supposant que vous connaissez les données démographiques assez bien). – tvanfosson

+0

@tvanfosson: Bien sûr; D'accord. – Pistos

+0

que je n'ai pas pensé à cela: P merci :) – youri

3

Divisez-le en plusieurs expressions. Par exemple (pseudo-code) ...

phone_no_patterns = [ 
    /[0-9]{13}/, # 00312
    /+(31|32)\(0\)\d{2}-\d{7}/ # +31(0)20-1234567 
    # ..etc.. 
] 
def check_number(num): 
    for pattern in phone_no_patterns: 
     if num matches pattern: 
      return match.groups 

Ensuite, on boucle un peu plus chaque motif, vérifier si chacun correspond à ..

Fractionnement les modèles en haut, en est facile de fixer des chiffres précis qui sont à l'origine problèmes (ce qui serait horrible avec le regex unique et monolithique)

2

Ce n'est pas une optimisation, mais vous utilisez

(-)?()? 

trois fois dans votre regex.Cela vous fera match sur les numéros de téléphone comme ceux-ci

+31(0)6-12345678 
+31(0)6 12345678 

mais aussi correspondre à des numéros contenant un tiret suivi d'un espace, comme

+31(0)6- 12345678 

Vous pouvez remplacer

(-)?()? 

avec

(-|)? 

pour correspondre à un tiret ou un espace.

+0

mieux encore '[-]?' –

+0

C'est mieux. Votre solution enregistre un personnage. Je me sauvais en tapant. :) –

+0

je n'ai pas remarqué que je l'ai fait merci – youri

3

(31 | 32) est mauvaise. Lors de la correspondance 32, le moteur regex essayera d'abord de faire correspondre 31 (2 caractères), échouer, et revenir en arrière deux caractères pour correspondre à 31. Il est plus efficace d'abord correspondre 3 (un caractère), essayer 1 (échec), revenir en arrière un caractère et match 2.

Bien sûr, votre regex échoue sur les numéros 0800; ils ne sont pas à 10 chiffres.

+0

je ne veux pas de numéros 0800 mais l'autre partie de votre commentaire était utile merci. – youri

Questions connexes