Je récemment une réponse à cette question sur Royaume - Uni pour les codes postaux de la langue de R . J'ai découvert que le modèle de regex du gouvernement britannique est incorrect et ne valide pas correctement certains codes postaux. Malheureusement, la plupart des réponses ici sont basées sur ce modèle incorrect.
Je vais décrire certains de ces problèmes ci-dessous et fournir une expression régulière révisée qui fonctionne réellement .
Remarque
Ma réponse (et les expressions régulières en général):
- Valide uniquement les formats de code postal .
- Ne garantit pas qu'un code postal existe légitimement .
- Pour cela, utilisez une API appropriée! Voir la réponse de Ben pour plus d'informations.
Si vous ne vous souciez pas de la mauvaise regex et que vous voulez simplement passer à la réponse, défiler jusqu'à la section Réponse .
Le mauvais Regex
Les expressions régulières de cette section ne doivent pas être utilisées.
C'est le regex défaillant que le gouvernement britannique a fourni aux développeurs (je ne sais pas combien de temps ce lien sera actif, mais vous pouvez le voir dans leur documentation de transfert de données en masse ):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Problèmes
Problème 1 - Copier / Coller
Voir regex utilisé ici .
Comme de nombreux développeurs le font probablement, ils copient / collent du code (en particulier les expressions régulières) et les collent en attendant qu'ils fonctionnent. Bien que cela soit excellent en théorie, cela échoue dans ce cas particulier car le copier / coller à partir de ce document change en fait l'un des caractères (un espace) en un caractère de nouvelle ligne comme indiqué ci-dessous:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))
[0-9][A-Za-z]{2})$
La première chose que la plupart des développeurs feront est simplement d'effacer la nouvelle ligne sans réfléchir à deux fois. Désormais, l'expression régulière ne correspondra pas aux codes postaux contenant des espaces (à partGIR 0AA
code postal).
Pour résoudre ce problème, le caractère de nouvelle ligne doit être remplacé par le caractère d'espace:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Problème 2 - Limites
Voir regex utilisé ici .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^ ^ ^ ^^
L'expression régulière de code postal ancre incorrectement l'expression régulière. Quiconque utilise cette expression régulière pour valider les codes postaux pourrait être surpris si une valeur commefooA11 1AA
que celle-ci passe. C'est parce qu'ils ont ancré le début de la première option et la fin de la deuxième option (indépendamment l'un de l'autre), comme indiqué dans l'expression régulière ci-dessus.
Cela signifie que ^
(affirme la position au début de la ligne) ne fonctionne que sur la première option ([Gg][Ii][Rr] 0[Aa]{2})
, donc la deuxième option validera toutes les chaînes qui se terminent par un code postal (indépendamment de ce qui précède).
De même, la première option n'est pas ancrée à la fin de la ligne $
, elle GIR 0AAfoo
est donc également acceptée.
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Pour résoudre ce problème, les deux options doivent être enveloppées dans un autre groupe (ou groupe non capturant) et les ancres placées autour de celui-ci:
^(([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))$
^^ ^^
Problème 3 - Jeu de caractères incorrect
Voir regex utilisé ici .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^
Le regex manque un -
ici pour indiquer une plage de caractères. En l'état, si un code postal est au format ANA NAA
(où A
représente une lettre et N
représente un nombre), et qu'il commence par autre chose que A
ouZ
, il échouera.
Cela signifie qu'il correspondra à A1A 1AA
et Z1A 1AA
, mais pas B1A 1AA
.
Pour résoudre ce problème, le caractère -
doit être placé entre le A
et Z
dans le jeu de caractères respectif:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Problème 4 - Jeu de caractères facultatif incorrect
Voir regex utilisé ici .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Je jure qu'ils n'ont même pas testé cette chose avant de la publier sur le Web. Ils ont rendu facultatif le mauvais jeu de caractères. Ils ont fait l' [0-9]
option dans la quatrième sous-option de l'option 2 (groupe 9). Cela permet à l'expression régulière de faire correspondre les codes postaux mal formatés commeAAA 1AA
.
Pour résoudre ce problème, rendez la classe de caractères suivante facultative à la place (et faites ensuite correspondre l'ensemble [0-9]
exactement une fois):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?)))) [0-9][A-Za-z]{2})$
^
Problème 5 - Performance
Les performances sur cette expression régulière sont extrêmement médiocres. Tout d'abord, ils ont placé l'option de modèle la moins susceptible de correspondre GIR 0AA
au début. Combien d'utilisateurs auront probablement ce code postal par rapport à tout autre code postal; probablement jamais? Cela signifie que chaque fois que l'expression régulière est utilisée, elle doit d'abord épuiser cette option avant de passer à l'option suivante. Pour voir comment les performances sont affectées, vérifiez le nombre d'étapes effectuées par l' expression régulière d' origine (35) par rapport à la même expression régulière après avoir inversé les options (22).
Le deuxième problème de performance est dû à la façon dont l'ensemble de l'expression régulière est structurée. Il est inutile de revenir en arrière sur chaque option si l'une d'entre elles échoue. La façon dont la regex actuelle est structurée peut être grandement simplifiée. Je propose un correctif pour cela dans la section Réponse .
Problème 6 - Espaces
Voir regex utilisé ici
Cela peut ne pas être considéré comme un problème en soi, mais cela inquiète la plupart des développeurs. Les espaces dans l'expression régulière ne sont pas facultatifs, ce qui signifie que les utilisateurs qui saisissent leurs codes postaux doivent placer un espace dans le code postal. C'est une solution facile en ajoutant simplement ?
après les espaces pour les rendre facultatifs. Consultez la section Réponse pour un correctif.
Répondre
1. Correction de l'expression régulière du gouvernement britannique
La résolution de tous les problèmes décrits dans la section Problèmes et la simplification du modèle donnent le modèle suivant, plus court et plus concis. Nous pouvons également supprimer la plupart des groupes puisque nous validons le code postal dans son ensemble (pas des parties individuelles):
Voir regex utilisé ici
^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$
Cela peut être raccourci en supprimant toutes les plages de l'un des cas (majuscules ou minuscules) et en utilisant un indicateur insensible à la casse. Remarque : certaines langues n'en ont pas, utilisez donc la plus longue ci-dessus. Chaque langage implémente différemment l'indicateur d'insensibilité à la casse.
Voir regex utilisé ici .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Plus court à nouveau en remplaçant [0-9]
par \d
(si votre moteur regex le prend en charge):
Voir regex utilisé ici .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Modèles simplifiés
Sans garantir des caractères alphabétiques spécifiques, les éléments suivants peuvent être utilisés (gardez à l'esprit les simplifications de 1. La correction de l'expression régulière du gouvernement britannique a également été appliquée ici):
Voir regex utilisé ici .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
Et encore plus si vous ne vous souciez pas du cas particulier GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Modèles compliqués
Je ne suggérerais pas de sur-vérification d'un code postal car de nouvelles zones, districts et sous-districts peuvent apparaître à tout moment. Ce que je vous suggère potentiellement faire, est ajouté le support pour le bord-cas. Certains cas particuliers existent et sont décrits dans cet article de Wikipédia .
Voici des expressions rationnelles complexes qui incluent les sous-sections de 3. (3.1, 3.2, 3.3).
En ce qui concerne les modèles de 1. Correction de l'expression régulière du gouvernement britannique :
Voir regex utilisé ici
^(([A-Z][A-HJ-Y]?\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
Et par rapport à 2. Modèles simplifiés :
Voir regex utilisé ici
^(([A-Z]{1,2}\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
3.1 Territoires britanniques d'outre-mer
L'article de Wikipedia indique actuellement (certains formats légèrement simplifiés):
AI-1111
: Anguila
ASCN 1ZZ
: Île de l'Ascension
STHL 1ZZ
: Sainte-Hélène
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Territoire britannique de l'océan Indien
BIQQ 1ZZ
: Territoire antarctique britannique
FIQQ 1ZZ
: Les îles Falkland
GX11 1ZZ
: Gibraltar
PCRN 1ZZ
: Îles Pitcairn
SIQQ 1ZZ
: Géorgie du Sud et îles Sandwich du Sud
TKCA 1ZZ
: Îles Turques-et-Caïques
BFPO 11
: Akrotiri et Dhekelia
ZZ 11
& GE CX
: Bermudes (selon ce document )
KY1-1111
: Iles Caïmans (selon ce document )
VG1111
: Îles Vierges britanniques (selon ce document )
MSR 1111
: Montserrat (d'après ce document )
Une expression régulière globale pour ne correspondre qu'aux territoires britanniques d'outre-mer pourrait ressembler à ceci:
Voir regex utilisé ici .
^((ASCN|STHL|TDCU|BBND|[BFS]IQQ|GX\d{2}|PCRN|TKCA) ?\d[A-Z]{2}|(KY\d|MSR|VG|AI)[ -]?\d{4}|(BFPO|[A-Z]{2}) ?\d{2}|GE ?CX)$
3.2 Bureau de poste des forces britanniques
Bien qu'ils aient été récemment modifiés pour mieux s'aligner sur le système de code postal britannique BF#
(où #
représente un nombre), ils sont considérés comme des codes postaux alternatifs facultatifs . Ces codes postaux suivent le format de BFPO
, suivi de 1 à 4 chiffres:
Voir regex utilisé ici
^BFPO ?\d{1,4}$
3.3 Père Noël?
Il y a un autre cas particulier avec le Père Noël (comme mentionné dans d'autres réponses): SAN TA1
c'est un code postal valide. Une regex pour cela est très simple:
^SAN ?TA1$