RegEx per la corrispondenza dei codici postali britannici


186

Sto cercando una regex che convaliderà un codice postale britannico complesso completo solo all'interno di una stringa di input. Tutti i moduli di codice postale non comuni devono essere coperti come al solito. Per esempio:

fiammiferi

  • CW3 9SS
  • SE5 0EG
  • SE50EG
  • se5 0eg
  • WC2H 7LT

Nessuna corrispondenza

  • aWC2H 7LT
  • WC2H 7LTa
  • WC2H

Come posso risolvere questo problema?


2
@axrwkr che non sembra utile
Kieran Benton,

8
Convalida del codice postale nel Regno Unito - JavaScript e PHP Non sono riuscito a ottenere la risposta accettata per abbinare codici postali validi, ma l'ho trovato e corrisponde a codici postali validi. Per la convalida lato client, la versione JavaScript può essere utilizzata così com'è, per la convalida lato server, riscrivere JavaScript come C # è abbastanza semplice. Riformatta anche il codice postale per avere uno spazio, quindi se inserisci un codice postale come W1A1AA, oltre alla convalida, lo riformatterà in W1A 1AA. Si occupa anche di codici postali insoliti in vari territori britannici.

2
Il link fornito non funziona per i formati "AA1A 1AA". Riferimento: dhl.com.tw/content/dam/downloads/tw/express/forms/…
Anthony Scaife,

1
Se si desidera semplicemente convalidare un codice postale, offriamo un endpoint API REST di convalida gratuito (iscrizione obbligatoria) - developers.alliescomputing.com/postcoder-web-api/address-lookup/…
Stephen Keable

1
Buona domanda. Penso che varrebbe la pena includere un codice postale centrale di Manchester come "M1 3HZ" nella tua lista di esempi insoliti che devono corrispondere. Molte persone non sono a conoscenza delle combinazioni di numeri 1 lettera 1.
Martin Joiner,

Risposte:


208

Consiglio di dare un'occhiata allo Standard dei dati governativi del Regno Unito per i codici postali [link ora morto; archivio di XML , vedere Wikipedia per la discussione]. C'è una breve descrizione dei dati e lo schema xml allegato fornisce un'espressione regolare. Potrebbe non essere esattamente quello che vuoi, ma sarebbe un buon punto di partenza. Il RegEx differisce leggermente dall'XML, in quanto un carattere P in terza posizione nel formato A9A 9AA è consentito dalla definizione fornita.

Il RegEx fornito dal governo del Regno Unito era:

([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]?))))\s?[0-9][A-Za-z]{2})

Come sottolineato nella discussione di Wikipedia, ciò consentirà alcuni codici postali non reali (ad esempio quelli che iniziano con AA, ZY) e forniranno un test più rigoroso che potresti provare.


52
E quel reg ex con uno spazio bianco opzionale tra i due segmenti (GIR 0AA) | (((([AZ- [QVX]] [0-9] [0-9]?) | ((([AZ- [QVX]] [AZ- [IJZ]] [0-9] [0-9]) |? (([AZ- [QVX]] [0-9] [A-HJKSTUW]) | ([AZ- [QVX]] [ AZ- [IJZ]] [0-9] [ABEHMNPRVWXY])))) \ s? [0-9] [AZ- [CIKMOV]] {2})
gb2d

7
Potrebbe essere una buona idea portare la vera regex alla risposta, dato che le pagine sembrano scadere ogni anno ...
pauloya,

7
Nota che questa regex è per XML Schema, che è, ovviamente, leggermente diverso dagli altri sapori regex
artbristol

6
Non riesco a farlo funzionare in JavaScript. Funziona solo con determinati motori regex?
NickG

12
In realtà lo hanno cambiato: Trasferimento di dati in^([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})$
blocco

85

Sembra che useremo ^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW]) ?[0-9][ABD-HJLNP-UW-Z]{2})$, che è una versione leggermente modificata di quella suggerita da Minglis sopra.

Tuttavia, dovremo esaminare esattamente quali sono le regole, poiché le varie soluzioni sopra elencate sembrano applicare regole diverse su quali lettere sono consentite.

Dopo alcune ricerche, abbiamo trovato ulteriori informazioni. Apparentemente una pagina su "govtalk.gov.uk" indica una specifica del codice postale govtalk-postcodes . Ciò indica uno schema XML in XML Schema che fornisce un'istruzione 'pseudo regex' delle regole del codice postale.

L'abbiamo preso e ci abbiamo lavorato un po 'per darci la seguente espressione:

^((GIR &0AA)|((([A-PR-UWYZ][A-HK-Y]?[0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]))) &[0-9][ABD-HJLNP-UW-Z]{2}))$

Questo rende gli spazi opzionali, ma ti limita a uno spazio (sostituisci '&' con '{0,} per spazi illimitati). Presuppone che tutto il testo debba essere in maiuscolo.

Se si desidera consentire le lettere minuscole, con un numero qualsiasi di spazi, utilizzare:

^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$

Questo non copre i territori d'oltremare e applica solo il formato, NON l'esistenza di aree diverse. Si basa sulle seguenti regole:

Può accettare i seguenti formati:

  • "GIR 0AA"
  • A9 9ZZ
  • A99 9ZZ
  • AB9 9ZZ
  • AB99 9ZZ
  • A9C 9ZZ
  • AD9E 9ZZ

Dove:

  • 9 può essere qualsiasi numero a una cifra.
  • A può essere qualsiasi lettera tranne Q, V o X.
  • B può essere qualsiasi lettera tranne I, J o Z.
  • C può essere qualsiasi lettera ad eccezione di I, L, M, N, O, P, Q, R, V, X, Y o Z.
  • D può essere qualsiasi lettera tranne I, J o Z.
  • E può essere uno qualsiasi di A, B, E, H, M, N, P, R, V, W, X o Y.
  • Z può essere qualsiasi lettera tranne C, I, K, M, O o V.

Auguri

Colin


2
Ottima risposta, ho aggiunto in quelli esteri^(([gG][iI][rR] {0,}0[aA]{2})|(([aA][sS][cC][nN]|[sS][tT][hH][lL]|[tT][dD][cC][uU]|[bB][bB][nN][dD]|[bB][iI][qQ][qQ]|[fF][iI][qQ][qQ]|[pP][cC][rR][nN]|[sS][iI][qQ][qQ]|[iT][kK][cC][aA]) {0,}1[zZ]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yxA-HK-XY]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$
David Bradshaw,

Perché specificare {0,}invece di *spazi illimitati e opzionali?
Codice Animal

76

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 0AAcodice 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 1AApassa 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 Arappresenta una lettera e Nrappresenta un numero), e inizia con qualcosa di diverso da Ao Z, fallirà.

Ciò significa che corrisponderà A1A 1AAe Z1A 1AA, ma non B1A 1AA.

Per risolvere questo problema, il personaggio -deve essere inserito tra Ae Znel 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 0AAall'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$

4
I modelli semplificati sono davvero un'ottima opzione da usare. Trovo che sia meglio non essere troppo restrittivi con una regex in quanto è necessario assicurarsi che sia aggiornato con eventuali modifiche o che si possano avere utenti molto arrabbiati. Mi sento meglio abbinarmi liberamente con una regex semplificata per eliminare gli errori evidenti e quindi applicare ulteriori controlli come una ricerca di indirizzi (o e-mail di conferma nel caso di regex e-mail) per confermare la validità.
James Coyle,

2
Analisi eccellente e approfondita.
Steve,

1
Risposta brillante su così tanti livelli. Alla fine, sono andato con il tuo secondo schema semplificato. Dato che in realtà ho un DB con tutti i codici postali del Regno Unito, ho solo bisogno di un primo passaggio per vedere se una stringa di indirizzo contiene potenzialmente un codice postale valido, quindi non mi interessano i falsi positivi (poiché la ricerca effettiva li sradicherà) , ma a me interessano i falsi negativi. E anche la velocità conta.
John Powell,

Ci sono così tanti problemi con il sistema postale britannico, manifestamente creato dal comitato prima dell'era dei computer, ma il problema della lunghezza e degli spazi variabili è uno dei più dannosi. Ho visto tutti i tipi di orrori, compresi i codici postali di riempimento come E1 5JX a E1 5JX, ovvero con tre spazi, in modo che si allinei perfettamente con SW18 5HA in Excel (inserire software di scelta orribilmente inappropriato per la gestione degli indirizzi). L'unica soluzione sana, IMHO, è quella di eliminare tutti gli spazi, in modo che il codice postale sia una singola stringa prima che arrivi ovunque vicino a Elastic, Solr, Postgres, ecc.
John Powell

45

Non esiste un'espressione regolare completa di codice postale nel Regno Unito in grado di convalidare un codice postale. Puoi verificare che un codice postale sia nel formato corretto usando un'espressione regolare; non che esista realmente.

I codici postali sono arbitrariamente complessi e in costante evoluzione. Ad esempio, il codice di uscita W1non ha e potrebbe non avere mai un numero compreso tra 1 e 99 per ogni area di codice postale.

Non puoi aspettarti cosa c'è attualmente per essere vero per sempre. Ad esempio, nel 1990, l'ufficio postale ha deciso che Aberdeen si stava un po 'affollando. Hanno aggiunto uno 0 alla fine di AB1-5 rendendolo AB10-50 e quindi creato un numero di codici postali tra questi.

Ogni volta che viene costruita una nuova strada, viene creato un nuovo codice postale. Fa parte del processo per ottenere il permesso di costruire; le autorità locali sono tenute a mantenerlo aggiornato con l'ufficio postale (non che lo facciano tutti).

Inoltre, come notato da molti altri utenti, ci sono i codici postali speciali come Girobank, GIR 0AA e quello per le lettere a Babbo Natale, SAN TA1 - probabilmente non vorrai postare nulla lì ma non sembra essere coperto da qualsiasi altra risposta.

Quindi, ci sono i codici postali BFPO, che ora stanno cambiando in un formato più standard . Entrambi i formati saranno validi. Infine, c'è la fonte Wikipedia dei territori d'oltremare .

+ ---------- + -------------------------------------- -------- +
| Codice postale | Posizione |
+ ---------- + -------------------------------------- -------- +
| AI-2640 | Anguilla |
| 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 1AA | Gibilterra |
| PCRN 1ZZ | Isole Pitcairn |
| SIQQ 1ZZ | Georgia del Sud e Isole Sandwich Meridionali |
| TKCA 1ZZ | Isole Turks e Caicos |
+ ---------- + -------------------------------------- -------- +

Successivamente, devi tenere conto del fatto che il Regno Unito ha "esportato" il suo sistema di codici postali in molti luoghi del mondo. Tutto ciò che convalida un codice postale "UK" convaliderà anche i codici postali di numerosi altri paesi.

Se si desidera convalidare un codice postale nel Regno Unito, il modo più sicuro per farlo è utilizzare una ricerca dei codici postali correnti. Esistono diverse opzioni:

  • Ordnance Survey rilascia Code-Point Open con licenza open data. Sarà leggermente indietro rispetto ai tempi ma è gratuito. Questo (probabilmente - non ricordo) non includerà i dati dell'Irlanda del Nord in quanto l'Ordnance Survey non ha alcuna competenza. La mappatura nell'Irlanda del Nord è condotta dal Ordnance Survey of Northern Ireland e hanno il loro prodotto puntatore separato, a pagamento . Puoi usarlo e aggiungere i pochi che non sono coperti abbastanza facilmente.

  • Royal Mail rilascia il codice di avviamento postale (PAF) , questo include BFPO che non sono sicuro che Code-Point Open faccia. Viene aggiornato regolarmente ma costa denaro (e a volte possono essere decisamente cattivi al riguardo). PAF include l'indirizzo completo anziché solo i codici postali e viene fornito con la sua guida per programmatori . L'Open Data User Group (ODUG) sta attualmente facendo pressioni affinché il PAF venga rilasciato gratuitamente, ecco una descrizione della loro posizione .

  • Infine, c'è AddressBase . Questa è una collaborazione tra Ordnance Survey, Autorità locali, Royal Mail e una società di corrispondenza per creare una directory definitiva di tutte le informazioni su tutti gli indirizzi del Regno Unito (hanno avuto anche abbastanza successo). È a pagamento, ma se lavori con un'autorità locale, un dipartimento governativo o un servizio governativo è gratuito per loro. Ci sono molte più informazioni oltre ai soli codici postali inclusi.


lo sguardo sembra interessante
SuperUberDuper

2
sebbene questa non sia la risposta che l'op stava cercando, è probabilmente la più utile. Questo mi incoraggerà a rilassare le regole di verifica che sto per fare.
John Hunt,

22

Ho dato un'occhiata ad alcune delle risposte di cui sopra e sconsiglio di utilizzare lo schema della risposta di @ Dan (c. 15 dic. 10) , poiché contrassegna erroneamente quasi lo 0,4% dei codici postali validi come non validi, mentre gli altri no .

Ordnance Survey fornisce un servizio chiamato Code Point Open che:

contiene un elenco di tutte le unità postali correnti in Gran Bretagna

Ho eseguito ciascuno dei regex sopra con l'elenco completo dei codici postali (6 luglio 13) da questi dati usando grep:

cat CSV/*.csv |
    # Strip leading quotes
    sed -e 's/^"//g' |
    # Strip trailing quote and everything after it
    sed -e 's/".*//g' |
    # Strip any spaces
    sed -E -e 's/ +//g' |
    # Find any lines that do not match the expression
    grep --invert-match --perl-regexp "$pattern"

Ci sono 1.686.202 codici postali in totale.

Di seguito sono riportati i numeri di codici postali validi che non corrispondono a ciascuno $pattern:

'^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]?[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$'
# => 6016 (0.36%)
'^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW]) ?[0-9][ABD-HJLNP-UW-Z]{2})$'
# => 0
'^GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|BX|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}$'
# => 0

Naturalmente, questi risultati riguardano solo codici postali validi che sono erroneamente contrassegnati come non validi. Così:

'^.*$'
# => 0

Non sto dicendo nulla su quale modello sia il migliore per quanto riguarda il filtraggio di codici postali non validi.


1
Non è questo ciò che dico nella mia risposta e se stai percorrendo la strada del disproof, dovresti probabilmente farli tutti e tenerlo aggiornato se qualcuno cambia la loro risposta? In caso contrario, fai almeno riferimento alla data dell'ultima modifica della risposta da cui hai ricevuto in modo che le persone possano vedere se è stata modificata da allora.
Ben

Punto valido. Modificato di conseguenza. Penso che si aggiunga alla discussione per sottolineare che la maggior parte di questi schemi non esclude nessuno dei codici CPO, ma che la risposta più votata (regex valida) lo fa. Lettori futuri: essere consapevoli del fatto che i miei risultati potrebbero non essere aggiornati.
RichardTowers,

17
^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$

Espressione regolare per abbinare codici postali validi nel Regno Unito. Nel sistema postale del Regno Unito non tutte le lettere sono utilizzate in tutte le posizioni (lo stesso con le targhe di immatricolazione dei veicoli) e ci sono varie regole per governare questo. Questa regex tiene conto di quelle regole. Dettagli delle regole: prima metà del codice postale Formati validi [AZ] [AZ] [0-9] [AZ] [AZ] [AZ] [0-9] [0-9] [AZ] [0-9] [ 0-9] [AZ] [AZ] [0-9] [AZ] [AZ] [AZ] [AZ] [0-9] [AZ] [AZ] [0-9] Posizione eccezioni - Prima. Contrapposizione - QVX non utilizzata Posizione - Seconda. Contrapposizione - IJZ non utilizzato tranne nella posizione GIR 0AA - Terza. Vincolo - AEHMNPRTVXY usato solo Posizione - Avanti. Contrapposizione - ABEHMNPRVWXY Seconda metà del codice postale Formati validi [0-9] [AZ] [AZ] Posizione eccezioni - Seconda e terza. Contrapposizione: CIKMOV non utilizzato

http://regexlib.com/REDetails.aspx?regexp_id=260


1
Non ho idea del perché le persone abbiano votato in negativo questa risposta - è la regex corretta
Ollie

Il regex non funziona con i codici postali "YO31" e "YO31 1" in Javscript.
Pratik Khadloya,

9
Non penso sia corretto, dato che il regex dato contraddice la descrizione e suggerisce che puoi avere codici postali a partire dal 0-9quale non puoi
Luigi Plinge,

4
Questo regex fallisce su circa 6000 codici postali validi, quindi lo sconsiglio. Vedere la mia risposta .
RichardTowers,

questo fallisce su qualsiasi codice postale in minuscolo o senza spazio per me
Ballerino

14

Secondo questa tabella di Wikipedia

inserisci qui la descrizione dell'immagine

Questo modello copre tutti i casi

(?:[A-Za-z]\d ?\d[A-Za-z]{2})|(?:[A-Za-z][A-Za-z\d]\d ?\d[A-Za-z]{2})|(?:[A-Za-z]{2}\d{2} ?\d[A-Za-z]{2})|(?:[A-Za-z]\d[A-Za-z] ?\d[A-Za-z]{2})|(?:[A-Za-z]{2}\d[A-Za-z] ?\d[A-Za-z]{2})

Quando lo usi su Android \ Java usa \\ d


Ho trovato questa la risposta più leggibile, anche se cerca solo la forma di un codice postale, piuttosto che i codici effettivi validi secondo le soluzioni che prendono le informazioni dal sito Web gov.uk, ma è abbastanza buono per il mio caso d'uso. Dopo averlo giocato un po '(in Python), l'ho considerato un regex leggermente più compatto ma equivalente che consente anche uno spazio opzionale: ([a-zA-Z] (?: (?: [A-zA- Z] \ d [a-zA-Z]) | (:? \ d {1,2}) | (:??? [a-zA-Z] \ d {1,2})) \ W [0 -9] [a-zA-Z] {2})
Richard J,

14

La maggior parte delle risposte qui non ha funzionato per tutti i codici postali che ho nel mio database. Ne ho finalmente trovato uno che convalida con tutti, usando la nuova regex fornita dal governo:

https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/413338/Bulk_Data_Transfer_-_additional_validation_valid_from_March_2015.pdf

Non si trova in nessuna delle risposte precedenti, quindi lo inserisco qui nel caso in cui riducano il link:

^([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})$

AGGIORNAMENTO: regex aggiornato come indicato da Jamie Bull. Non sono sicuro se si trattasse del mio errore durante la copia o di un errore nella regex del governo, il link non funziona ora ...

AGGIORNAMENTO: Come trovato ctwheels, questo regex funziona con il sapore regex javascript. Vedi il suo commento per uno che funziona con il sapore di pcre (php).


1
^([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})$dovrebbe essere ^([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})$- trova la differenza ;-)
Jamie Bull,

1
Spot on! Aggiornato la mia risposta. Grazie!
Jesús Carrera,

2
Questa è l'unica risposta qui che ha funzionato in regexr.com e Notepad ++. Anche se, l'ho cambiato in ([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})(rimosso ^e $aggiunto un ?dopo lo spazio) per regexr.com per trovare più di un risultato e per entrambi per trovare un risultato che non ha un separatore di spazio.
mythofechelon,

@ctwheels questo regex è per il sapore javascript. Se il tuo collegamento non riuscito selezioni javascript funzionerà. È una grande cattura e aggiornerò la mia risposta.
Jesús Carrera,

1
La regex pubblicata nella documentazione è intrinsecamente errata. L'intera espressione dovrebbe essere racchiusa in un gruppo che non cattura (?:)e quindi posizionare le ancore attorno ad essa. Vedilo fallire qui . Per ulteriori informazioni, vedere la mia risposta 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})|(([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}))$è l'espressione regolare corretta.
Ruote

12

Un vecchio post, ma ancora piuttosto alto nei risultati di Google, quindi ho pensato di aggiornare. Questo documento del 14 ottobre definisce l'espressione regolare del codice postale nel Regno Unito come:

^([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})|(([**AZ**a-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})$

a partire dal:

https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/359448/4__Bulk_Data_Transfer_-_additional_validation_valid.pdf

Il documento spiega anche la logica che sta dietro. Tuttavia, ha un errore (in grassetto) e consente anche lettere minuscole, che sebbene legale non sia normale, quindi versione modificata:

^(GIR 0AA)|((([A-Z][0-9]{1,2})|(([A-Z][A-HJ-Y][0-9]{1,2})|(([A-Z][0-9][A-Z])|([A-Z][A-HJ-Y][0-9]?[A-Z])))) [0-9][A-Z]{2})$

Funziona con i nuovi codici postali di Londra (ad es. W1D 5LH) che le versioni precedenti non avevano.


Sembra che l'errore che hai evidenziato in grassetto sia stato corretto nel documento, ma preferisco comunque la tua espressione regolare in quanto è più facile da leggere.
Professore di programmazione il

5
L'unica cosa che direi è rendere lo spazio opzionale cambiando lo spazio in \ s? poiché lo spazio non è un requisito necessario per la leggibilità.
Professore di programmazione il

La regex pubblicata nella documentazione è intrinsecamente errata. L'intera espressione dovrebbe essere racchiusa in un gruppo che non cattura (?:)e quindi posizionare le ancore attorno ad essa. Vedilo fallire qui . Per ulteriori informazioni, vedere la mia risposta 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})|(([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}))$è l'espressione regolare corretta.
Ruote

10

Questa è la regex offerta da Google sul proprio dominio i18napis.appspot.com :

GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|BX|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}

10

I codici postali sono soggetti a modifiche e l'unico vero modo per convalidare un codice postale è disporre dell'elenco completo dei codici postali e vedere se è presente.

Ma le espressioni regolari sono utili perché:

  • sono facili da usare e da implementare
  • sono corti
  • sono veloci da eseguire
  • sono abbastanza facili da mantenere (rispetto a un elenco completo di codici postali)
  • rileva ancora la maggior parte degli errori di input

Ma le espressioni regolari tendono ad essere difficili da mantenere, soprattutto per chi non l'ha inventato in primo luogo. Quindi deve essere:

  • il più facile da capire possibile
  • prova relativamente futura

Ciò significa che la maggior parte delle espressioni regolari in questa risposta non sono abbastanza buone. Ad esempio, vedo che [A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]corrisponderà a un'area di codice postale del modulo AA1A, ma sarà un dolore al collo se e quando viene aggiunta una nuova area di codice postale, perché è difficile capire a quali aree del codice postale corrisponde.

Voglio anche che la mia espressione regolare corrisponda alla prima e alla seconda metà del codice postale come corrispondenze tra parentesi.

Quindi ho pensato a questo:

(GIR(?=\s*0AA)|(?:[BEGLMNSW]|[A-Z]{2})[0-9](?:[0-9]|(?<=N1|E1|SE1|SW1|W1|NW1|EC[0-9]|WC[0-9])[A-HJ-NP-Z])?)\s*([0-9][ABD-HJLNP-UW-Z]{2})

Nel formato PCRE può essere scritto come segue:

/^
  ( GIR(?=\s*0AA) # Match the special postcode "GIR 0AA"
    |
    (?:
      [BEGLMNSW] | # There are 8 single-letter postcode areas
      [A-Z]{2}     # All other postcode areas have two letters
      )
    [0-9] # There is always at least one number after the postcode area
    (?:
      [0-9] # And an optional extra number
      |
      # Only certain postcode areas can have an extra letter after the number
      (?<=N1|E1|SE1|SW1|W1|NW1|EC[0-9]|WC[0-9])
      [A-HJ-NP-Z] # Possible letters here may change, but [IO] will never be used
      )?
    )
  \s*
  ([0-9][ABD-HJLNP-UW-Z]{2}) # The last two letters cannot be [CIKMOV]
$/x

Per me questo è il giusto equilibrio tra la convalida il più possibile, mentre allo stesso tempo è a prova di futuro e consente una facile manutenzione.


Non sono sicuro del motivo per cui sei stato votato verso il basso - questo funziona con tutti i codici postali validi che ho lanciato e spazi che molte delle risposte sopra non gestiscono correttamente. Qualcuno vorrebbe spiegare perché?
Jon,

1
@Jon Corrisponde anche quando altri personaggi vengono aggiunti all'inizio o alla fine, ad es. aSW1A 1AAasfgAbbinati per me (non ho effettuato il downgrade anche se sembra che possa essere risolto facilmente)
decimale

9

Ho cercato un codice postale britannico regex per l'ultimo giorno circa e sono incappato in questo thread. Ho esaminato la maggior parte dei suggerimenti di cui sopra e nessuno di loro ha funzionato per me, quindi ho trovato la mia regex che, per quanto ne so, cattura tutti i codici postali britannici validi a partire da gennaio '13 (secondo l'ultima letteratura di la Royal Mail).

Di seguito sono riportati il ​​regex e alcuni semplici codici postali che controllano il codice PHP. NOTA: - Consente codici postali minuscoli o maiuscoli e l'anomalia GIR 0AA ma per gestire la presenza, probabilmente più probabile, di uno spazio nel mezzo di un codice postale inserito, utilizza anche un semplice str_replace per rimuovere lo spazio prima del test contro la regex. Eventuali discrepanze oltre a ciò e la stessa Royal Mail non le menzionano nemmeno nella loro letteratura (vedere http://www.royalmail.com/sites/default/files/docs/pdf/programmers_guide_edition_7_v5.pdf e iniziare a leggere da pagina 17) !

Nota: nella letteratura della Royal Mail (link sopra) c'è una leggera ambiguità che circonda la terza e la quarta posizione e le eccezioni in atto se questi personaggi sono lettere. Ho contattato Royal Mail direttamente per chiarirlo e con le loro stesse parole "Una lettera in quarta posizione del codice esterno con il formato AANA NAA non ha eccezioni e le eccezioni di terza posizione si applicano solo all'ultima lettera del codice esterno con il formato ANA NAA. " Direttamente dalla bocca del cavallo!

<?php

    $postcoderegex = '/^([g][i][r][0][a][a])$|^((([a-pr-uwyz]{1}([0]|[1-9]\d?))|([a-pr-uwyz]{1}[a-hk-y]{1}([0]|[1-9]\d?))|([a-pr-uwyz]{1}[1-9][a-hjkps-uw]{1})|([a-pr-uwyz]{1}[a-hk-y]{1}[1-9][a-z]{1}))(\d[abd-hjlnp-uw-z]{2})?)$/i';

    $postcode2check = str_replace(' ','',$postcode2check);

    if (preg_match($postcoderegex, $postcode2check)) {

        echo "$postcode2check is a valid postcode<br>";

    } else {

        echo "$postcode2check is not a valid postcode<br>";

    }

?>

Spero che aiuti chiunque altro a trovare questo thread in cerca di una soluzione.


1
Sarei curioso di sapere quali esempi di codici postali non hanno superato quello pubblicato?
Zhaph - Ben Duguid,

Non posso darti un codice postale specifico (senza avere accesso all'elenco PAF completo) ma i codici postali con il formato ANA NAA potrebbero potenzialmente fallire poiché le lettere P e Q sono consentite in terza posizione e i codici postali con il formato AANA NAA potrebbero potenzialmente falliscono anche perché la 4a posizione consente tutte le lettere (la regex data nella risposta accettata sopra non tiene conto di nessuna di queste due). Come ho detto, seguirò solo i consigli attuali della Royal Mail - al momento della risposta sopra, forse quel regex era pienamente conforme.
Dan Solo

Grazie per l'heads-up - Vedo che "P" sembra essere stato aggiunto come accettabile nella terza posizione (dal tuo documento collegato), ma non Q - ma dove stai leggendo che "la 4a posizione consente tutte le lettere"? Il documento non menziona affatto la "quarta posizione" per quanto posso vedere, quindi la leggerei come "la terza lettera indipendentemente dalla posizione effettiva".
Zhaph - Ben Duguid,

1
Ho appena ricevuto una risposta dal team di supporto di Royal Mail e la mia interpretazione delle regole è apparentemente corretta. Una lettera in quarta posizione del codice esterno (ad esempio AANA NAA) non ha eccezioni e le eccezioni di terza posizione si applicano solo all'ultima lettera (ad esempio ANA NAA). Direttamente dalla bocca del cavallo.
Dan Solo,

1
@DanSolo Questo regex restituirà una vera corrispondenza per la prima metà di un codice postale valido mancante del codice interno, ad esempio SW1Ao BD25senza la seconda metà (o almeno lo ha fatto per me)
decimale

7

Ecco una regex basata sul formato specificato nei documenti che sono collegati alla risposta di marcj:

/^[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-Z]{2}$/

L'unica differenza tra questo e le specifiche è che gli ultimi 2 caratteri non possono essere in [CIKMOV] secondo le specifiche.

Modifica: ecco un'altra versione che verifica le limitazioni dei caratteri finali.

/^[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-BD-HJLNP-UW-Z]{2}$/

Ci sono molte più complessità in un codice postale nel Regno Unito che non solo accettare A-Z- Qnon è mai permesso, Vviene usato solo con parsimonia, ecc. A seconda della posizione del personaggio.
Zhaph - Ben Duguid,

2
Ciò potrebbe essere irrilevante se ciò che si desidera è un controllo della sintassi. Come molti altri hanno osservato, solo una ricerca in un database aggiornato diventa quasi corretta, e anche allora c'è il problema di quanto sia aggiornato il database. Quindi, per me, questa regex del checker di sintassi è chiara, semplice e utile.
Rick-777,

5

Alcuni dei regex sopra sono un po 'restrittivi. Notare il codice postale originale: "W1K 7AA" non verrebbe eseguito, dato che la regola "Posizione 3 - AEHMNPRTVXY utilizzata solo" sopra come "K" non sarebbe consentita.

la regex:

^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$

Sembra un po 'più accurato, vedi l' articolo di Wikipedia intitolato "Codici postali nel Regno Unito" .

Nota che questa regex richiede solo caratteri maiuscoli.

La domanda più grande è se stai limitando l'input dell'utente per consentire solo i codici postali effettivamente esistenti o se stai semplicemente cercando di impedire agli utenti di immettere immondizia completa nei campi del modulo. Abbinare correttamente ogni possibile codice postale e verificarlo in futuro è un puzzle più difficile e probabilmente non ne vale la pena a meno che tu non sia un HMRC.


Sembra che l'ufficio postale sia passato, ma il governo è un po 'indietro :(
Zhaph - Ben Duguid

4
Io uso questo: "^ ([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,1} [0-9] [ A-Za-z] {2}) $ "Mi piace perché consente l'uso di maiuscole e minuscole e rende lo spazio opzionale - migliore per l'usabilità, se non corretto al 100%!
bigtv,

4

ecco come abbiamo risolto il problema con il codice postale nel Regno Unito:

^([A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]?[ ]?)([0-9]{1}[A-Za-z]{2})$

Spiegazione:

  • aspettarsi 1 o 2 caratteri az, multa superiore o inferiore
  • aspettarsi 1 o 2 numeri
  • aspettarsi 0 o 1 carattere az, multa superiore o inferiore
  • spazio opzionale consentito
  • aspettarsi 1 numero
  • aspettarsi 2 az, superiore o inferiore fine

Questo ottiene la maggior parte dei formati, quindi utilizziamo il db per verificare se il codice postale è effettivamente reale, questi dati sono guidati da openpoint https://www.ordnancesurvey.co.uk/opendatadownload/products.html

spero che questo ti aiuti


Ciò consente il formato AANNA NAA, che non è valido.
Ruote ct

Da qui la parte della risposta "Questo ottiene la maggior parte dei formati". :)
Alex Stephens,

4

Regole di base:

^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2}$

I codici postali nel Regno Unito (o codici postali, come vengono chiamati) sono composti da cinque a sette caratteri alfanumerici separati da uno spazio. Le regole che coprono quali personaggi possono apparire in particolari posizioni sono piuttosto complicate e piene di eccezioni. L'espressione regolare appena mostrata si attiene quindi alle regole di base.

Regole complete:

Se hai bisogno di una regex che spunta tutte le caselle per le regole del codice postale a spese della leggibilità, ecco qui:

^(?:(?:[A-PR-UWYZ][0-9]{1,2}|[A-PR-UWYZ][A-HK-Y][0-9]{1,2}|[A-PR-UWYZ][0-9][A-HJKSTUW]|[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]) [0-9][ABD-HJLNP-UW-Z]{2}|GIR 0AA)$

Fonte: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s16.html

Testato contro il database dei nostri clienti e sembra perfettamente accurato.


4

Uso il regex seguente che ho testato con tutti i codici postali validi nel Regno Unito. È basato sulle regole raccomandate, ma condensato quanto ragionevole e non fa uso di alcuna regola regex specifica per una lingua specifica.

([A-PR-UWYZ]([A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y])?|[0-9]([0-9]|[A-HJKPSTUW])?) ?[0-9][ABD-HJLNP-UW-Z]{2})

Presuppone che il codice postale sia stato convertito in maiuscolo e che non contenga caratteri iniziali o finali, ma accetterà uno spazio opzionale tra il codice esterno e il codice.

Il codice postale speciale "GIR0 0AA" è escluso e non verrà convalidato in quanto non è nell'elenco ufficiale dei codici postali dell'ufficio postale e, per quanto ne so, non verrà utilizzato come indirizzo registrato. L'aggiunta dovrebbe essere banale come caso speciale, se necessario.


4

Volevo un regex semplice, dove va bene permettere troppo, ma non negare un codice postale valido. Sono andato con questo (l'input è una stringa spogliata / tagliata):

/^([a-z0-9]\s*){5,8}$/i

Ciò consente i codici postali più brevi possibili come "L1 8JQ" e quelli più lunghi come "OL14 5ET".

Poiché consente fino a 8 caratteri, consentirà anche codici postali errati di 8 caratteri se non c'è spazio: "OL145ETX". Ma ancora una volta, questa è una regex semplicistica, per quando va abbastanza bene.


Oh, mi scuso. Penso di aver perso il / i mentre stavo testando ieri.
Giovanni

3

Prima metà del codice postale Formati validi

  • [AZ] [AZ] [0-9] [AZ]
  • [AZ] [AZ] [0-9] [0-9]
  • [AZ] [0-9] [0-9]
  • [AZ] [AZ] [0-9]
  • [AZ] [AZ] [AZ]
  • [AZ] [0-9] [AZ]
  • [AZ] [0-9]

Eccezioni
Posizione 1 - QVX non utilizzato
Posizione 2 - IJZ non utilizzato ad eccezione di GIR 0AA
Posizione 3 - AEHMNPRTVXY utilizzato solo
Posizione 4 - ABEHMNPRVWXY

Seconda metà del codice postale

  • [0-9] [AZ] [AZ]

Eccezioni
Posizione 2 + 3 - CIKMOV non utilizzato

Ricorda che non vengono utilizzati tutti i codici possibili, quindi questo elenco è una condizione necessaria ma non sufficiente per un codice valido. Potrebbe essere più semplice abbinare solo un elenco di tutti i codici validi?


3

Per verificare che un codice postale sia in un formato valido secondo la guida del programmatore della Royal Mail :

          |----------------------------outward code------------------------------| |------inward code-----|
#special↓       α1        α2    AAN  AANA      AANN      AN    ANN    ANA (α3)        N         AA
^(GIR 0AA|[A-PR-UWYZ]([A-HK-Y]([0-9][A-Z]?|[1-9][0-9])|[1-9]([0-9]|[A-HJKPSTUW])?) [0-9][ABD-HJLNP-UW-Z]{2})$

Tutti i codici postali su doogal.co.uk corrispondono, tranne quelli non più in uso.

Aggiunta di uno ?spazio dopo e utilizzo della corrispondenza senza distinzione tra maiuscole e minuscole per rispondere a questa domanda:

'se50eg'.match(/^(GIR 0AA|[A-PR-UWYZ]([A-HK-Y]([0-9][A-Z]?|[1-9][0-9])|[1-9]([0-9]|[A-HJKPSTUW])?) ?[0-9][ABD-HJLNP-UW-Z]{2})$/ig);
Array [ "se50eg" ]

3

Questo consente spazi vuoti e schede da entrambi i lati nel caso in cui non si desideri fallire la convalida e quindi tagliarla da un lato.

^\s*(([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,1}[0-9][A-Za-z]{2})\s*$)

Questo è l'unico modello che ha funzionato per me usando c # (System.Text.RegularExpressions) con gli esempi descritti nella domanda originale
MattjeS

Questa è la regex rotta del governo britannico e non riesce a convalidare alcuni dei formati validi.
Ruote ct

@ctwheels Ciao, potresti fornire un codice postale in errore, grazie.
Matas Vaitkevicius,

Ad esempio, AAA 1AAnon è un formato valido: vedi la mia risposta per una spiegazione e una correzione.
Ruote ct

2

Per aggiungere a questo elenco un regex più pratico che uso che consente all'utente di inserire un empty stringè:

^$|^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,1}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$

Questa regex consente lettere maiuscole e minuscole con uno spazio opzionale in mezzo

Dal punto di vista degli sviluppatori software questa regex è utile per i software in cui un indirizzo può essere facoltativo. Ad esempio se un utente non voleva fornire i dettagli del proprio indirizzo


1

Dai un'occhiata al codice Python in questa pagina:

http://www.brunningonline.net/simon/blog/archives/001292.html

Ho un po 'di codice postale da analizzare. Il requisito è piuttosto semplice; Devo analizzare un codice postale in un codice postale e (facoltativo) in codice. La buona notizia è che non devo eseguire alcuna convalida: devo solo tagliare ciò che mi è stato fornito in modo vagamente intelligente. Non posso assumere molto sulla mia importazione in termini di formattazione, ad esempio case e spazi incorporati. Ma questa non è una brutta notizia; la cattiva notizia è che devo fare tutto in gioco di ruolo. :-(

Tuttavia, ho messo insieme una piccola funzione Python per chiarire il mio pensiero.

L'ho usato per elaborare i codici postali per me.


1

Ci è stata data una specifica:

I codici postali britannici devono avere una delle seguenti forme (con un'eccezione, vedi sotto): 
    § A9 9AA 
    § A99 9AA
    § AA9 9AA
    § AA99 9AA
    § A9A 9AA
    § AA9A 9AA
dove A rappresenta un carattere alfabetico e 9 rappresenta un carattere numerico.
Regole aggiuntive si applicano ai caratteri alfabetici, come segue:
    § Il personaggio in posizione 1 non può essere Q, V o X
    § Il personaggio nella posizione 2 non può essere I, J o Z
    § Il personaggio in posizione 3 non può essere I, L, M, N, O, P, Q, R, V, X, Y o Z
    § Il personaggio in posizione 4 non può essere C, D, F, G, I, J, K, L, O, Q, S, T, U o Z
    § I caratteri nelle due posizioni più a destra non possono essere C, I, K, M, O o V
L'unica eccezione che non segue queste regole generali è il codice postale "GIR 0AA", che è un codice postale speciale valido.

Abbiamo pensato a questo:

/^([A-PR-UWYZ][A-HK-Y0-9](?:[A-HJKS-UW0-9][ABEHMNPRV-Y0-9]?)?\s*[0-9][ABD-HJLNP-UW-Z]{2}|GIR\s*0AA)$/i

Ma nota: questo consente un numero qualsiasi di spazi tra i gruppi.


2
paulslater19, purtroppo la tua soluzione consente codici postali A99A 9AA.

1

Ho la regex per la convalida del codice postale nel Regno Unito.

Questo funziona per tutti i tipi di codice postale interno o esterno

^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))) || ^((GIR)[ ]?(0AA))$|^(([A-PR-UWYZ][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][0-9][A-HJKS-UW0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9][ABEHMNPRVWXY0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$

Funziona con tutti i tipi di formato.

Esempio:

AB10 --------------------> SOLO CODICE POST ESTERNO

A1 1AA ------------------> COMBINAZIONE DEL CODICE POST (ESTERNO E INTERNO)

WC2A --------------------> ESTERNO


1

La risposta accettata riflette le regole fornite da Royal Mail, sebbene ci sia un refuso nella regex. Questo errore di battitura sembra essere stato presente anche sul sito gov.uk (come nella pagina dell'archivio XML).

Nel formato A9A 9AA le regole consentono un carattere P in terza posizione, mentre la regex non lo consente. Il regex corretto sarebbe:

(GIR 0AA)|((([A-Z-[QVX]][0-9][0-9]?)|(([A-Z-[QVX]][A-Z-[IJZ]][0-9][0-9]?)|(([A-Z-[QVX]][0-9][A-HJKPSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2}) 

Abbreviando questo si ottiene la seguente regex (che utilizza la sintassi Perl / Ruby):

(GIR 0AA)|([A-PR-UWYZ](([0-9]([0-9A-HJKPSTUW])?)|([A-HK-Y][0-9]([0-9ABEHMNPRVWXY])?))\s?[0-9][ABD-HJLNP-UW-Z]{2})

Include anche uno spazio opzionale tra il primo e il secondo blocco.


1

Quello che ho trovato in quasi tutte le varianti e la regex dal pdf di trasferimento di massa e ciò che è sul sito di Wikipedia è questo, in particolare per la regex di Wikipedia, ci deve essere un ^ dopo la prima | (barra verticale). L'ho capito testando per AA9A 9AA, altrimenti il ​​controllo del formato per A9A 9AA lo convaliderà. Ad esempio, il controllo di EC1D 1BB che deve essere non valido ritorna valido perché C1D 1BB è un formato valido.

Ecco cosa ho escogitato per un buon regex:

^([G][I][R] 0[A]{2})|^((([A-Z-[QVX]][0-9]{1,2})|([A-Z-[QVX]][A-HK-Y][0-9]{1,2})|([A-Z-[QVX]][0-9][ABCDEFGHJKPSTUW])|([A-Z-[QVX]][A-HK-Y][0-9][ABEHMNPRVWXY])) [0-9][A-Z-[CIKMOV]]{2})$

1

Attraverso test e osservazioni empiriche, oltre a confermare con https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation , ecco la mia versione di un regex Python che analizza e convalida correttamente un codice postale nel Regno Unito:

UK_POSTCODE_REGEX = r'(?P<postcode_area>[A-Z]{1,2})(?P<district>(?:[0-9]{1,2})|(?:[0-9][A-Z]))(?P<sector>[0-9])(?P<postcode>[A-Z]{2})'

Questa regex è semplice e ha gruppi di acquisizione. Essa non include tutte le convalide di legale codici postali del Regno Unito, ma prende in considerazione solo la lettera vs posizioni numero.

Ecco come lo userei nel codice:

@dataclass
class UKPostcode:
    postcode_area: str
    district: str
    sector: int
    postcode: str

    # https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation
    # Original author of this regex: @jontsai
    # NOTE TO FUTURE DEVELOPER:
    # Verified through empirical testing and observation, as well as confirming with the Wiki article
    # If this regex fails to capture all valid UK postcodes, then I apologize, for I am only human.
    UK_POSTCODE_REGEX = r'(?P<postcode_area>[A-Z]{1,2})(?P<district>(?:[0-9]{1,2})|(?:[0-9][A-Z]))(?P<sector>[0-9])(?P<postcode>[A-Z]{2})'

    @classmethod
    def from_postcode(cls, postcode):
        """Parses a string into a UKPostcode

        Returns a UKPostcode or None
        """
        m = re.match(cls.UK_POSTCODE_REGEX, postcode.replace(' ', ''))

        if m:
            uk_postcode = UKPostcode(
                postcode_area=m.group('postcode_area'),
                district=m.group('district'),
                sector=m.group('sector'),
                postcode=m.group('postcode')
            )
        else:
            uk_postcode = None

        return uk_postcode


def parse_uk_postcode(postcode):
    """Wrapper for UKPostcode.from_postcode
    """
    uk_postcode = UKPostcode.from_postcode(postcode)
    return uk_postcode

Ecco i test unitari:

@pytest.mark.parametrize(
    'postcode, expected', [
        # https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation
        (
            'EC1A1BB',
            UKPostcode(
                postcode_area='EC',
                district='1A',
                sector='1',
                postcode='BB'
            ),
        ),
        (
            'W1A0AX',
            UKPostcode(
                postcode_area='W',
                district='1A',
                sector='0',
                postcode='AX'
            ),
        ),
        (
            'M11AE',
            UKPostcode(
                postcode_area='M',
                district='1',
                sector='1',
                postcode='AE'
            ),
        ),
        (
            'B338TH',
            UKPostcode(
                postcode_area='B',
                district='33',
                sector='8',
                postcode='TH'
            )
        ),
        (
            'CR26XH',
            UKPostcode(
                postcode_area='CR',
                district='2',
                sector='6',
                postcode='XH'
            )
        ),
        (
            'DN551PT',
            UKPostcode(
                postcode_area='DN',
                district='55',
                sector='1',
                postcode='PT'
            )
        )
    ]
)
def test_parse_uk_postcode(postcode, expected):
    uk_postcode = parse_uk_postcode(postcode)
    assert(uk_postcode == expected)

0

Avevo bisogno di una versione che funzionasse in SAS con le PRXMATCHfunzioni correlate e, quindi mi è venuta in mente questa:

^[A-PR-UWYZ](([A-HK-Y]?\d\d?)|(\d[A-HJKPSTUW])|([A-HK-Y]\d[ABEHMNPRV-Y]))\s?\d[ABD-HJLNP-UW-Z]{2}$

Casi di prova e note:

/* 
Notes
The letters QVX are not used in the 1st position.
The letters IJZ are not used in the second position.
The only letters to appear in the third position are ABCDEFGHJKPSTUW when the structure starts with A9A.
The only letters to appear in the fourth position are ABEHMNPRVWXY when the structure starts with AA9A.
The final two letters do not use the letters CIKMOV, so as not to resemble digits or each other when hand-written.
*/

/*
    Bits and pieces
    1st position (any):         [A-PR-UWYZ]         
    2nd position (if letter):   [A-HK-Y]
    3rd position (A1A format):  [A-HJKPSTUW]
    4th position (AA1A format): [ABEHMNPRV-Y]
    Last 2 positions:           [ABD-HJLNP-UW-Z]    
*/


data example;
infile cards truncover;
input valid 1. postcode &$10. Notes &$100.;
flag = prxmatch('/^[A-PR-UWYZ](([A-HK-Y]?\d\d?)|(\d[A-HJKPSTUW])|([A-HK-Y]\d[ABEHMNPRV-Y]))\s?\d[ABD-HJLNP-UW-Z]{2}$/',strip(postcode));
cards;
1  EC1A 1BB  Special case 1
1  W1A 0AX   Special case 2
1  M1 1AE    Standard format
1  B33 8TH   Standard format
1  CR2 6XH   Standard format
1  DN55 1PT  Standard format
0  QN55 1PT  Bad letter in 1st position
0  DI55 1PT  Bad letter in 2nd position
0  W1Z 0AX   Bad letter in 3rd position
0  EC1Z 1BB  Bad letter in 4th position
0  DN55 1CT  Bad letter in 2nd group
0  A11A 1AA  Invalid digits in 1st group
0  AA11A 1AA  1st group too long
0  AA11 1AAA  2nd group too long
0  AA11 1AAA  2nd group too long
0  AAA 1AA   No digit in 1st group
0  AA 1AA    No digit in 1st group
0  A 1AA     No digit in 1st group
0  1A 1AA    Missing letter in 1st group
0  1 1AA     Missing letter in 1st group
0  11 1AA    Missing letter in 1st group
0  AA1 1A    Missing letter in 2nd group
0  AA1 1     Missing letter in 2nd group
;
run;

0

Il metodo seguente controlla il codice postale e fornisce informazioni complete

const valid_postcode = postcode => {
    try {
        postcode = postcode.replace(/\s/g, "");
        const fromat = postcode
            .toUpperCase()
            .match(/^([A-Z]{1,2}\d{1,2}[A-Z]?)\s*(\d[A-Z]{2})$/);
        const finalValue = `${fromat[1]} ${fromat[2]}`;
        const regex = /^([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;
        return {
            isValid: regex.test(postcode),
            formatedPostCode: finalValue,
            error: false,
            info: 'It is a valid postcode'
        };
    } catch (error) {
        return { error: true , info: 'Invalid post code has been entered!'};
    }
};
valid_postcode('GU348RR')
result => {isValid: true, formatedPostCode: "GU34 8RR", error: false, info: "It is a valid postcode"}
valid_postcode('sdasd4746asd')
result => {error: true, info: "Invalid post code has been entered!"}
valid_postcode('787898523')
result => {error: true, info: "Invalid post code has been entered!"}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.