Recentemente ho inviato una risposta a questa domanda su codici postali del Regno Unito per la lingua R . Ho scoperto che il modello regex del governo britannico non è corretto e non riesce a convalidare correttamente alcuni codici postali. Sfortunatamente, molte delle risposte qui si basano su questo modello errato.
Descriverò alcuni di questi problemi di seguito e fornirò un'espressione regolare rivista che funziona davvero .
Nota
La mia risposta (ed espressioni regolari in generale):
- Convalida solo i formati di codice postale .
- Non garantisce l' esistenza di un codice postale legittimo .
Se non ti interessa la cattiva regex e vuoi solo saltare alla risposta, scorri verso il basso fino alla sezione Risposta .
Il cattivo regex
Le espressioni regolari in questa sezione non devono essere utilizzate.
Questa è la regex fallita che il governo del Regno Unito ha fornito agli sviluppatori (non sono sicuro di quanto tempo rimarrà questo collegamento, ma puoi vederlo nella loro documentazione sul trasferimento di dati in blocco ):
^([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})$
I problemi
Problema 1 - Copia / Incolla
Vedi regex in uso qui .
Come molti sviluppatori probabilmente fanno, copiano / incollano il codice (specialmente le espressioni regolari) e li incollano aspettandosi che funzionino. Anche se questo è ottimo in teoria, fallisce in questo caso particolare perché copiare / incollare da questo documento in realtà cambia uno dei caratteri (uno spazio) in un carattere di nuova riga come mostrato di seguito:
^([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 prima cosa che farà la maggior parte degli sviluppatori è semplicemente cancellare la nuova riga senza pensarci due volte. Ora il regex non abbinerà i codici postali con spazi (diversi dal GIR 0AA
codice postale).
Per risolvere questo problema, il carattere di nuova riga deve essere sostituito con il carattere spazio:
^([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})$
^
Problema 2 - Confini
Vedi regex in uso qui .
^([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})$
^^ ^ ^ ^^
Il codice postale regex fissa erroneamente il regex. Chiunque utilizzi questa regex per convalidare i codici postali potrebbe essere sorpreso se fooA11 1AA
passa un valore come . Questo perché hanno ancorato l'inizio della prima opzione e la fine della seconda opzione (indipendentemente l'una dall'altra), come sottolineato nella regex sopra.
Ciò significa che ^
(afferma la posizione all'inizio della riga) funziona solo sulla prima opzione ([Gg][Ii][Rr] 0[Aa]{2})
, quindi la seconda opzione convaliderà tutte le stringhe che terminano in un codice postale (indipendentemente da ciò che precede).
Allo stesso modo, la prima opzione non è ancorata alla fine della linea $
, quindi GIR 0AAfoo
è anche accettata.
^([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})$
Per risolvere questo problema, entrambe le opzioni dovrebbero essere racchiuse in un altro gruppo (o gruppo non di acquisizione) e le ancore posizionate attorno a quello:
^(([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}))$
^^ ^^
Problema 3 - Set di caratteri impropri
Vedi regex in uso qui .
^([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})$
^^
Nel regex manca un -
qui per indicare un intervallo di caratteri. Allo stato attuale, se un codice postale è nel formato ANA NAA
(dove A
rappresenta una lettera e N
rappresenta un numero), e inizia con qualcosa di diverso da A
o Z
, fallirà.
Ciò significa che corrisponderà A1A 1AA
e Z1A 1AA
, ma non B1A 1AA
.
Per risolvere questo problema, il personaggio -
deve essere inserito tra A
e Z
nel rispettivo set di caratteri:
^([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})$
^
Problema 4 - Set di caratteri opzionale errato
Vedi regex in uso qui .
^([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})$
^
Giuro che non hanno nemmeno testato questa cosa prima di pubblicizzarla sul web. Hanno reso facoltativo il set di caratteri sbagliato. Hanno fatto [0-9]
un'opzione nella quarta sub-opzione dell'opzione 2 (gruppo 9). Ciò consente a regex di abbinare codici postali come erroneamente formattati AAA 1AA
.
Per risolvere questo problema, rendere facoltativa la classe di caratteri successiva (e successivamente far [0-9]
corrispondere il set esattamente una volta):
^([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})$
^
Problema 5 - Prestazioni
Le prestazioni su questa regex sono estremamente scarse. Prima di tutto, hanno posizionato l'opzione di pattern meno probabile da abbinare GIR 0AA
all'inizio. Quanti utenti avranno probabilmente questo codice postale rispetto a qualsiasi altro codice postale; probabilmente mai? Ciò significa che ogni volta che si utilizza regex, è necessario esaurire questa opzione prima di passare all'opzione successiva. Per vedere come le prestazioni sono influenzate, controlla il numero di passi compiuti dalla regex originale (35) rispetto alla stessa regex dopo aver capovolto le opzioni (22).
Il secondo problema con le prestazioni è dovuto al modo in cui l'intera regex è strutturata. Non ha senso tornare indietro su ogni opzione se si fallisce. Il modo in cui l'attuale regex è strutturato può essere notevolmente semplificato. Fornisco una soluzione per questo nella sezione Risposta .
Problema 6 - Spazi
Vedi regex in uso qui
Questo non può essere considerato di per sé un problema , ma solleva preoccupazioni per la maggior parte degli sviluppatori. Gli spazi nel regex non sono facoltativi, il che significa che gli utenti che inseriscono i loro codici postali devono inserire uno spazio nel codice postale. Questa è una soluzione semplice semplicemente aggiungendo ?
dopo gli spazi per renderli opzionali. Vedi la sezione Risposta per una correzione.
Risposta
1. Correzione del Regex del governo britannico
Risolvendo tutti i problemi descritti nella sezione Problemi e semplificando il modello si ottiene il modello seguente, più breve e più conciso. Possiamo anche rimuovere la maggior parte dei gruppi poiché stiamo convalidando il codice postale nel suo insieme (non singole parti):
Vedi regex in uso qui
^([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})$
Questo può essere ulteriormente abbreviato rimuovendo tutti gli intervalli da uno dei casi (maiuscolo o minuscolo) e usando un flag insensibile al maiuscolo / minuscolo. Nota : alcune lingue non ne hanno una, quindi usa quella più lunga sopra. Ogni lingua implementa il flag insensibilità maiuscole / minuscole in modo diverso.
Vedi regex in uso qui .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Più breve sostituzione di nuovo [0-9]
con \d
(se il tuo motore regex lo supporta):
Vedi regex in uso qui .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Pattern semplificati
Senza garantire caratteri alfabetici specifici, è possibile utilizzare quanto segue (tenere presente le semplificazioni da 1. La correzione del Regex del governo del Regno Unito è stata applicata anche qui):
Vedi regex in uso qui .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
E ancora di più se non ti interessa il caso speciale GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Modelli complicati
Non suggerirei una verifica eccessiva di un codice postale poiché nuove aree, distretti e sottodistretti potrebbero apparire in qualsiasi momento. Quello che suggerirò di fare potenzialmente è l'aggiunta del supporto per casi limite. Alcuni casi speciali esistono e sono descritti in questo articolo di Wikipedia .
Ecco regex complesse che includono le sottosezioni di 3. (3.1, 3.2, 3.3).
In relazione ai modelli in 1. Fissare il Regex del governo del Regno Unito :
Vedi regex in uso qui
^(([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)$
E in relazione a 2. Modelli semplificati :
Vedi regex in uso qui
^(([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 Territori britannici d'oltremare
L'articolo di Wikipedia attualmente afferma (alcuni formati leggermente semplificati):
AI-1111
: Anguila
ASCN 1ZZ
: Isola dell'Ascensione
STHL 1ZZ
: Sant'Elena
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Territorio britannico dell'Oceano Indiano
BIQQ 1ZZ
: Territorio antartico britannico
FIQQ 1ZZ
: Isole Falkland
GX11 1ZZ
: Gibilterra
PCRN 1ZZ
: Isole Pitcairn
SIQQ 1ZZ
: Georgia del Sud e Isole Sandwich Meridionali
TKCA 1ZZ
: Isole Turks e Caicos
BFPO 11
: Akrotiri e Dhekelia
ZZ 11
& GE CX
: Bermuda (secondo questo documento )
KY1-1111
: Isole Cayman (secondo questo documento )
VG1111
: Isole Vergini britanniche (secondo questo documento )
MSR 1111
: Montserrat (secondo questo documento )
Una regex onnicomprensiva per abbinare solo i Territori britannici d'oltremare potrebbe apparire così:
Vedi regex in uso qui .
^((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 Ufficio postale delle forze britanniche
Anche se sono stati recentemente modificati per allinearli meglio con il sistema postale britannico BF#
(dove #
rappresenta un numero), sono considerati codici postali alternativi opzionali . Questi codici postali seguono (a cura di) il formato di BFPO
, seguito da 1-4 cifre:
Vedi regex in uso qui
^BFPO ?\d{1,4}$
3.3 Babbo Natale?
C'è un altro caso speciale con Babbo Natale (come menzionato in altre risposte): SAN TA1
è un codice postale valido. Un regex per questo è molto semplicemente:
^SAN ?TA1$