Cum se analizează o adresă stradală/postală de formă liberă din text și în componente (Programare, Api, Parsing, Adresa Străzii)

Matt a intrebat.

Facem afaceri în mare parte în Statele Unite și încercăm să îmbunătățim experiența utilizatorului prin combinarea tuturor câmpurilor de adresă într-o singură zonă de text. Dar există câteva probleme:

  • Adresa pe care o introduce utilizatorul poate să nu fie corectă sau să nu aibă un format standard.
  • Adresa trebuie să fie separată în părți (stradă, oraș, stat etc.) pentru a procesa plățile cu cardul de credit
  • Utilizatorii pot introduce mai mult decât adresa lor (cum ar fi numele sau compania cu care lucrează)
  • Google poate face acest lucru, dar Termenii de serviciu și limitele de interogare sunt prohibitive, mai ales cu un buget restrâns

Aparent, aceasta este o întrebare comună:

  • Script PHP pentru a analiza adresa?
  • Cum se analizează adresa în format liber pentru a o salva în baza de date
  • parser de adrese poștale în java
  • O modalitate mai eficientă de a extrage componentele adresei
  • Cum pot afișa o adresă poștală prepopulată în ecranul de contacte cu strada, orașul, codul poștal pe android
  • PHP regexp adresa SUA

Există o modalitate de a izola o adresă de textul din jurul ei și de a o rupe în bucăți? Există o expresie regulată pentru a analiza adresele?

Comentarii

  • Răspunsurile de mai jos sunt mai utile pentru că nu ignoră problema globală – faptul că adresele nu se încadrează într-un model comun. –  > Por Marc Maxmeister.
7 răspunsuri
Matt

Am văzut această întrebare de multe ori când am lucrat pentru o companie de verificare a adreselor. Postez răspunsul aici pentru a-l face mai accesibil programatorilor care caută cu aceeași întrebare. Compania la care am lucrat a procesat miliarde de adrese și am învățat multe în acest proces.

În primul rând, trebuie să înțelegem câteva lucruri despre adrese.

Adresele nu sunt obișnuite

Acest lucru înseamnă că expresiile regulate sunt eliminate. Am văzut de toate, de la simple expresii regulate care se potrivesc cu adrese într-un format foarte specific, până la aceasta:

/s+(d{2,5}s+)(?![a|p]mb)(([a-zA-Z|s+]{1,5}){1,2})?([s|,|.]+)?(([a-zA-Z|s+]{1,30}){1,4})(court|ct|street|st|drive|dr|lane|ln|road|rd|blvd)([s|,|.|;]+)?(([a-zA-Z|s+]{1,30}){1,2})([s|,|.]+)? b(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|GU|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VI|VT|WA|WI|WV|WY)([s|,|.]+)?(s+d{5})?([s|,|.]+)/i

… la acest unde un fișier de peste 900 de linii generează din mers o expresie regulată supermasivă pentru a se potrivi și mai mult. Nu le recomand pe acestea (de exemplu, iată o copie a regex-ului de mai sus, care face o mulțime de greșeli). Nu există o formulă magică ușoară pentru a face ca acest lucru să funcționeze. În teorie și de teorie, este imposibil să se potrivească adrese cu o expresie regulată.

Publicația USPS 28 documentează numeroasele formate de adrese care sunt posibile, cu toate cuvintele-cheie și variațiile lor. Mai rău decât atât, adresele sunt adesea ambigue. Cuvintele pot avea mai multe semnificații („St” poate fi „Saint” sau „Street”) și există cuvinte pe care sunt aproape sigur că le-au inventat. (Cine știa că „Stravenue” este un sufix de stradă?).

Ar fi nevoie de un cod care să înțeleagă cu adevărat adresele, iar dacă acel cod există, este un secret comercial. Dar probabil că ai putea să ți-l faci singur, dacă ești foarte interesat de asta.

Adresele vin în forme și dimensiuni neașteptate

Iată câteva adrese inventate (dar complete):

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Chiar și acestea sunt posibil valabile:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

Evident, acestea nu sunt standardizate. Punctuația și întreruperile de rând nu sunt garantate. Iată ce se întâmplă:

  1. Numărul 1 este complet, deoarece conține o adresă stradală, un oraș și un stat. Cu aceste informații, sunt suficiente pentru a identifica adresa și poate fi considerată „livrabilă” (cu o anumită standardizare).

  2. Numărul 2 este completă deoarece conține o adresă stradală (cu numărul secundar/unitatea de cazare) și un cod poștal din 5 cifre, ceea ce este suficient pentru a identifica o adresă.

  3. Numărul 3 este un format complet de cutie poștală, deoarece conține un cod poștal.

  4. Numărul 4 este, de asemenea, complet, deoarece codul poștal este unic, , ceea ce înseamnă că o entitate privată sau o corporație a cumpărat acel spațiu de adrese. Un cod poștal unic este destinat spațiilor de livrare cu volum mare sau concentrate. Tot ceea ce este adresat la codul poștal 12345 ajunge la General Electric din Schenectady, NY. Acest exemplu nu va ajunge la nimeni în mod special, dar USPS îl va livra în continuare.

  5. Numărul 5 este, de asemenea, complet, fie că vă vine să credeți sau nu. Doar cu aceste numere, adresa completă poate fi descoperită atunci când este comparată cu o bază de date cu toate adresele posibile. Completarea direcțiilor lipsă, a indicativului secundar și a codului ZIP+4 este banală atunci când vedeți fiecare număr ca pe o componentă. Iată cum arată, complet extinsă și standardizată:

205 N 1105 W Apt 14

Beverly Hills CA 90210-5221

Datele de adresă nu vă aparțin

În majoritatea țărilor care furnizează date oficiale privind adresele furnizorilor licențiați, datele privind adresele în sine aparțin agenției guvernamentale. În SUA, adresele sunt deținute de USPS. Același lucru este valabil și pentru Canada Post, Royal Mail și altele, deși fiecare țară aplică sau definește proprietatea puțin diferit. Cunoașterea acestui lucru este importantă, deoarece, de obicei, se interzice efectuarea de inginerie inversă a bazei de date de adrese. Trebuie să aveți grijă cum achiziționați, stocați și utilizați datele.

Google Maps este o soluție obișnuită pentru remedierea rapidă a adreselor, însă TOS este destul de prohibitivă; de exemplu, nu puteți utiliza datele sau API-urile lor fără a afișa o hartă Google și numai în scopuri necomerciale (cu excepția cazului în care plătiți) și nu puteți stoca datele (cu excepția memorării temporare în memoria cache). Are sens. Datele Google sunt unele dintre cele mai bune din lume. Cu toate acestea, Google Maps nu nu verifică adresa. Dacă o adresă nu există, vă va arăta totuși unde se află adresa. ar putea fi ar fi fost dacă ar fi ar exista exista (încercați pe propria stradă; utilizați un număr de casă despre care știți că nu există). Acest lucru este util uneori, dar fiți atenți la acest lucru.

Nominatim’s politica de utilizare este la fel de limitativă, în special pentru volum mare și pentru utilizarea comercială, iar datele provin în mare parte din surse gratuite, deci nu sunt la fel de bine întreținute (așa cum este natura proiectelor deschise). Cu toate acestea, s-ar putea totuși să se potrivească nevoilor dumneavoastră. O comunitate excelentă îl susține.

USPS însuși are un API, dar aceasta se întrerupe foarte des și nu vine cu garanții și nici cu suport. De asemenea, ar putea fi greu de utilizat. Unii oameni o folosesc cu moderație și fără probleme. Dar este ușor să nu observi că USPS cere să folosești API-ul lor doar pentru a confirma adresele de expediere prin intermediul lor.

Oamenii se așteaptă ca adresele să fie greu de găsit

Din păcate, am condiționat societatea noastră să se aștepte ca adresele să fie complicate. Există zeci de articole bune de UX peste tot pe internet despre acest lucru. Cu toate acestea, adevărul este că, dacă aveți un formular de adrese cu câmpuri individuale, la asta se așteaptă utilizatorii, chiar dacă îngreunează situația pentru adresele de limită care nu se încadrează în formatul așteptat de formular sau poate că formularul cere un câmp pe care nu ar trebui să îl ceară. Sau utilizatorii nu știu unde să pună o anumită parte a adresei lor.

Aș putea să continui la nesfârșit cu privire la UX-ul prost al formularelor de checkout din zilele noastre, dar în schimb, voi spune că combinarea adreselor într-un singur câmp va fi un bun venit schimbare binevenită – oamenii vor putea să își scrie adresa așa cum cred ei de cuviință, în loc să încerce să înțeleagă formularul tău lung. Cu toate acestea, această schimbare va fi neașteptată și utilizatorii ar putea să o găsească puțin deranjantă la început. Fiți conștienți de acest lucru.

O parte din această durere poate fi atenuată prin plasarea câmpului „țară” în față, înainte de adresă. Atunci când aceștia completează mai întâi câmpul de țară, știți cum să faceți să apară formularul dvs. Poate că aveți o modalitate bună de a trata adresele din SUA cu un singur câmp, astfel încât, dacă ei selectează Statele Unite, puteți reduce formularul la un singur câmp, altfel afișați câmpurile componente. Sunt doar lucruri la care trebuie să vă gândiți!

Acum știm de ce este greu; ce puteți face în acest sens?

USPS acordă licențe furnizorilor printr-un proces numit CASS™ Certification pentru a furniza adrese verificate clienților. Acești furnizori au acces la baza de date a USPS, actualizată lunar. Software-ul lor trebuie să se conformeze unor standarde riguroase pentru a fi certificat și, de multe ori, nu necesită acordul unor termeni limitativi precum cei discutați mai sus.

Multe companii certificate CASS pot procesa liste sau au API-uri: Melissa Data, Experian QAS și SmartyStreets, pentru a numi câteva.

(Din cauza faptului că am primit reproșuri pentru „publicitate”, mi-am trunchiat răspunsul în acest punct. Depinde de dvs. să găsiți o soluție care să funcționeze pentru dvs.).

Adevărul: Serios, oameni buni, eu nu lucrez la niciuna dintre aceste companii. Nu este o reclamă.

Comentarii

  • @Brian – Poate pentru că utilizatorul a furnizat o mulțime de informații utile pentru cei care citesc întrebarea și răspunsul, indiferent dacă aleg sau nu să folosească produsul companiei sale. –  > Por Zarepheth.
  • @Brian Aceste site-uri sunt scraperi de conținut. Ei mooching conținut pentru a obține poziții în SERP. Nu le-am mai văzut niciodată. Nu am postat niciodată acest conținut înainte sau după nicăieri altundeva. –  > Por Matt.
  • @khuderm Am observat abia acum, când am citit comentariul tău, că toate comentariile divergente au dispărut; nu știu sigur cum/când s-a întâmplat asta. Dar oricum, vezi istoricul de editare al răspunsului meu și vei găsi o referință directă la un extractor de adrese din SUA care te-ar putea ajuta. L-am construit când am lucrat la ultimul meu loc de muncă, dar este un cod proprietar, așa că nu-l pot împărtăși… dar există. Sperăm că este de ajutor. –  > Por Matt.
  • @Sayka Nu mai am. Acest post este vechi de 3 ani. –  > Por Matt.
  • Oops. Scuze @Matt. Ei bine, am început să te urmăresc prin întrebările tale și, de asemenea, Github. Sunteți destul de impresionant. –  > Por Abdul Saleem.
David Portabella

libpostal: o bibliotecă open-source pentru a analiza adrese, antrenându-se cu date din OpenStreetMap, OpenAddresses și OpenCage.

https://github.com/openvenues/libpostal(mai multe informații despre aceasta)

Alte instrumente/servicii:

John Nagle

Există multe analizatoare de adrese stradale. Există două variante de bază – cele care au baze de date cu nume de localități și nume de străzi și cele care nu au.

Un analizor de adrese stradale cu expresie regulată poate ajunge la o rată de succes de aproximativ 95% fără prea multe probleme. Apoi începi să te confrunți cu cazuri neobișnuite. Cel din Perl din CPAN, „Geo::StreetAddress::US”, este cam atât de bun. Există portări Python și Javascript ale acestuia, toate cu sursă deschisă. Am o versiune îmbunătățită în Python, care crește ușor rata de succes prin tratarea mai multor cazuri. Totuși, pentru a obține ultimul procent de 3%, aveți nevoie de baze de date care să vă ajute la dezambiguizare.

O bază de date cu coduri poștale din 3 cifre și nume și abrevieri ale statelor americane este de mare ajutor. Atunci când un analizator vede un cod poștal și un nume de stat coerente, poate începe să se fixeze pe format. Acest lucru funcționează foarte bine pentru SUA și Regatul Unit.

Analiza corectă a adreselor de stradă începe de la sfârșit și funcționează invers. Acesta este modul în care o fac sistemele USPS. Adresele sunt cel mai puțin ambigue la sfârșit, unde numele țării, numele orașelor și codurile poștale sunt relativ ușor de recunoscut. Numele străzilor pot fi, de obicei, izolate. Locațiile de pe străzi sunt cele mai complexe de analizat; aici se întâlnesc lucruri precum „Fifth Floor” și „Staples Pavillion”. În acest caz, o bază de date este de mare ajutor.

Comentarii

  • Există, de asemenea, modulul CPAN Lingua:EN::AddressParse. Deși este mai lent decât „Geo::StreetAddress::US”, oferă o rată de succes mai mare. –  > Por Kim Ryan.
Ervin Ruci

UPDATE: Geocode.xyz funcționează acum la nivel mondial. Pentru exemple, consultați https://geocode.xyz

Pentru SUA, Mexic și Canada, consultați geocoder.ca.

De exemplu:

Input: ceva se întâmplă în apropierea intersecției dintre main și arthur kill rd new york

Ieșire:

<geodata>
  <latt>40.5123510000</latt>
  <longt>-74.2500500000</longt>
  <AreaCode>347,718</AreaCode>
  <TimeZone>America/New_York</TimeZone>
  <standard>
    <street1>main</street1>
    <street2>arthur kill</street2>
    <stnumber/>
    <staddress/>
    <city>STATEN ISLAND</city>
    <prov>NY</prov>
    <postal>11385</postal>
    <confidence>0.9</confidence>
  </standard>
</geodata>

Puteți, de asemenea, să verificați rezultatele în interfața web sau să obțineți ieșirea sub formă de Json sau Jsonp. de exemplu. Caut restaurante în apropiere de 123 Main Street, New York

Comentarii

  • Cum ați implementat sistemul de analiză a adreselor folosind openaddress? Folosiți o strategie de forță brută? –  > Por Nithin K Anil.
  • Ce înțelegeți prin „forță brută”? Descompunerea textului în toate combinațiile posibile de șiruri de adrese posibile și compararea fiecăreia cu o bază de date de adrese nu este practică și va dura mult mai mult timp pentru a oferi un răspuns decât o face acest sistem. Openaddresses este una dintre sursele de date pentru crearea unui „set de antrenament” de formate de adrese pentru algoritm. Acesta utilizează aceste informații pentru a analiza adresele din textul nestructurat. –  > Por Ervin Ruci.
  • Un alt sistem similar este Geo::libpostal ( perltricks.com/article/announcing-geo–libpostal ) De asemenea, se pare că folosesc openstreetmap și openaddresses, pentru a construi șabloane de adrese din mers –  > Por Ervin Ruci.
  • Tocmai am testat geoparserul geocode.xyz (trimite text, primește înapoi locația) pe sute de adrese reale. Având în vedere un side by side cu API-ul google map și un set global de adrese, geocode.xyz‘s scantext a eșuat de cele mai multe ori. Întotdeauna a ales „Geneva, SUA” în locul lui „Geneva, Elveția” și, în general, a fost înclinată spre SUA. –  > Por Marc Maxmeister.
  • Depinde de context. geocode.xyz/?scantext=Geneva,%20Switzerland va produce: Locația meciului Geneva, Elveția, CH Scorul de încredere: 0.8 în timp ce geocode.xyz/?scantext=Geneva,%20USA va produce: Match Location Geneva,US Confidence Score: 1.0 De asemenea, puteți să regionalizați părtinirea după cum urmează: geocode.xyz/?scantext=Geneva,%20USA&region=CH –  > Por Ervin Ruci.
nothingisnecessary

Nu aveți cod? Să vă fie rușine!

Iată un simplu parser de adrese în JavaScript. Este destul de îngrozitor pentru fiecare dintre motivele pe care Matt le dă în disertația sa de mai sus (cu care sunt aproape 100% de acord: adresele sunt tipuri complexe, iar oamenii fac greșeli; mai bine externalizați și automatizați acest lucru – atunci când vă puteți permite).

Dar, în loc să plâng, am decis să încerc:

Acest cod funcționează OK pentru analizarea majorității rezultatelor Esri pentru findAddressCandidate și, de asemenea, cu alte geocodificatoare (inverse) care returnează adrese pe o singură linie în care strada/orașul/statul sunt delimitate prin virgule. Puteți extinde dacă doriți sau puteți scrie analizoare specifice fiecărei țări. Sau folosiți acest lucru ca studiu de caz pentru a arăta cât de dificil poate fi acest exercițiu sau cât de prost mă pricep eu la JavaScript. Recunosc că am petrecut doar aproximativ 30 de minute pe acest lucru (iterațiile viitoare ar putea adăuga cache-uri, validare zip și căutări de stat, precum și contextul locației utilizatorului), dar a funcționat pentru cazul meu de utilizare: Utilizatorul final vede un formular care analizează răspunsul la căutarea geocodificată în 4 căsuțe de text. Dacă analizarea adresei iese greșit (ceea ce este rar, cu excepția cazului în care datele sursă au fost slabe), nu este mare lucru – utilizatorul poate verifica și repara! (Dar pentru soluțiile automatizate, ar putea fie să respingă/ignoră, fie să marcheze ca eroare, astfel încât dezvoltatorul să poată fie să suporte noul format, fie să repare datele sursă).

Comentarii

  • declarație de renunțare: clienții mei sunt proprietarii datelor lor de adrese și rulează propriile servere Esri. Dacă preluați date de la Google, OSM, ArcGisOnline sau de oriunde, asigurați-vă că este în regulă să le stocați și să le folosiți (multe servicii au restricții privind modul în care puteți stoca și pentru cât timp) –  > Por nothingisnecessary.
  • Primul răspuns de mai sus prezintă un caz convingător că această problemă nu poate fi rezolvată cu regexuri dacă aveți de-a face cu o listă globală de adrese. 200 de țări au prea multe excepții. În cadrul testelor mele, puteți determina țara dintr-un șir de caractere destul de fiabil, apoi căutați un regex specific pentru fiecare țară – care este probabil modul în care funcționează API-urile mai bune. –  > Por Marc Maxmeister.
theBuzzyCoder

Pentru analizarea adreselor din SUA,

prefer să folosesc pachetul usaddress care este disponibil în pip doar pentru usaddress.

python3 -m pip install usaddress

Documentație
PyPi

Acest lucru a funcționat bine pentru mine pentru adresa SUA.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# address_parser.py
import sys
from usaddress import tag
from json import dumps, loads

if __name__ == '__main__':
    tag_mapping = {
        'Recipient': 'recipient',
        'AddressNumber': 'addressStreet',
        'AddressNumberPrefix': 'addressStreet',
        'AddressNumberSuffix': 'addressStreet',
        'StreetName': 'addressStreet',
        'StreetNamePreDirectional': 'addressStreet',
        'StreetNamePreModifier': 'addressStreet',
        'StreetNamePreType': 'addressStreet',
        'StreetNamePostDirectional': 'addressStreet',
        'StreetNamePostModifier': 'addressStreet',
        'StreetNamePostType': 'addressStreet',
        'CornerOf': 'addressStreet',
        'IntersectionSeparator': 'addressStreet',
        'LandmarkName': 'addressStreet',
        'USPSBoxGroupID': 'addressStreet',
        'USPSBoxGroupType': 'addressStreet',
        'USPSBoxID': 'addressStreet',
        'USPSBoxType': 'addressStreet',
        'BuildingName': 'addressStreet',
        'OccupancyType': 'addressStreet',
        'OccupancyIdentifier': 'addressStreet',
        'SubaddressIdentifier': 'addressStreet',
        'SubaddressType': 'addressStreet',
        'PlaceName': 'addressCity',
        'StateName': 'addressState',
        'ZipCode': 'addressPostalCode',
    }
    try:
        address, _ = tag(' '.join(sys.argv[1:]), tag_mapping=tag_mapping)
    except:
        with open('failed_address.txt', 'a') as fp:
            fp.write(sys.argv[1] + '
')
        print(dumps({}))
    else:
        print(dumps(dict(address)))

Rularea programului address_parser.py

 python3 address_parser.py 9757 East Arcadia Ave. Saugus MA 01906
 {"addressStreet": "9757 East Arcadia Ave.", "addressCity": "Saugus", "addressState": "MA", "addressPostalCode": "01906"}

Jeremy Thompson

Am venit târziu la petrecere, iată un script Excel VBA pe care l-am scris cu ani în urmă pentru Australia. Poate fi modificat cu ușurință pentru a sprijini alte țări. Am făcut un depozit GitHub al codului C# aici. L-am găzduit pe site-ul meu și îl puteți descărca de aici: http://jeremythompson.net/rocks/ParseAddress.xlsm

Strategia

Pentru orice țară cu un cod poștal care este numeric sau care poate fi asociat cu un RegEx, strategia mea funcționează foarte bine:

  1. Mai întâi detectăm numele și prenumele, care se presupune că sunt pe linia de sus. Este ușor să săriți peste nume și să începeți cu adresa prin debifarea căsuței de selectare (numită „Name is top row”, așa cum se arată mai jos).

  2. În continuare, se poate presupune că adresa, compusă din stradă și număr, este precedată de suburbie, iar St, Pde, Ave, Av, Rd, Cres, loop, etc. este un separator.

  3. Detectarea suburbiei în comparație cu statul și chiar cu țara poate păcăli cele mai sofisticate analizoare, deoarece pot exista conflicte. Pentru a depăși această problemă, folosesc o căutare a codului poștal bazată pe faptul că, după ce am eliminat numerele străzilor și ale apartamentelor/unităților, precum și PoBox, Ph,Fax, etc., va rămâne doar numărul de cod poștal. Acesta este ușor de comparat cu un regEx pentru a căuta apoi suburbia (suburbii) și țara.

Serviciul național al oficiului poștal vă va furniza gratuit o listă de coduri poștale cu suburbii și state, pe care o puteți stoca într-o foaie Excel, un tabel db, un fișier text/json/xml etc.

  1. În cele din urmă, deoarece unele coduri poștale au mai multe suburbii, verificăm ce suburbie apare în adresă.

Exemplu

Codul VBA

DISCLAIMER, știu că acest cod nu este perfect, sau chiar scris bine, totuși este foarte ușor de convertit în orice limbaj de programare și de rulat în orice tip de aplicație. strategia este răspunsul în funcție de țara și regulile dumneavoastră, luați acest cod ca exemplu:

Option Explicit

Private Const TopRow As Integer = 0

Public Sub ParseAddress()
Dim strArr() As String
Dim sigRow() As String
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim Stat As String
Dim SpaceInName As Integer
Dim Temp As String
Dim PhExt As String

On Error Resume Next

Temp = ActiveSheet.Range("Address")

'Split info into array
strArr = Split(Temp, vbLf)

'Trim the array
For i = 0 To UBound(strArr)
strArr(i) = VBA.Trim(strArr(i))
Next i

'Remove empty items/rows    
ReDim sigRow(LBound(strArr) To UBound(strArr))
For i = LBound(strArr) To UBound(strArr)
    If Trim(strArr(i)) <> "" Then
        sigRow(j) = strArr(i)
        j = j + 1
    End If
Next i
ReDim Preserve sigRow(LBound(strArr) To j)

'Find the name (MUST BE ON THE FIRST ROW UNLESS CHECKBOX UNTICKED)
i = TopRow
If ActiveSheet.Shapes("chkFirst").ControlFormat.Value = 1 Then

SpaceInName = InStr(1, sigRow(i), " ", vbTextCompare) - 1

If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
ActiveSheet.Range("FirstName") = VBA.Left(sigRow(i), SpaceInName)
Else
 If MsgBox("First Name: " & VBA.Mid$(sigRow(i), 1, SpaceInName), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("FirstName") = VBA.Left(sigRow(i), SpaceInName)
End If

If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
ActiveSheet.Range("Surname") = VBA.Mid(sigRow(i), SpaceInName + 2)
Else
  If MsgBox("Surame: " & VBA.Mid(sigRow(i), SpaceInName + 2), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Surname") = VBA.Mid(sigRow(i), SpaceInName + 2)
End If
sigRow(i) = ""
End If

'Find the Street by looking for a "St, Pde, Ave, Av, Rd, Cres, loop, etc"
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 8
    If InStr(1, VBA.UCase(sigRow(i)), Street(j), vbTextCompare) > 0 Then

    'Find the position of the street in order to get the suburb
    SpaceInName = InStr(1, VBA.UCase(sigRow(i)), Street(j), vbTextCompare) + Len(Street(j)) - 1

    'If its a po box then add 5 chars
    If VBA.Right(Street(j), 3) = "BOX" Then SpaceInName = SpaceInName + 5

    If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
    ActiveSheet.Range("Street") = VBA.Mid(sigRow(i), 1, SpaceInName)
    Else
      If MsgBox("Street Address: " & VBA.Mid(sigRow(i), 1, SpaceInName), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Street") = VBA.Mid(sigRow(i), 1, SpaceInName)
    End If
    'Trim the Street, Number leaving the Suburb if its exists on the same line
    sigRow(i) = VBA.Mid(sigRow(i), SpaceInName) + 2
    sigRow(i) = Replace(sigRow(i), VBA.Mid(sigRow(i), 1, SpaceInName), "")

    GoTo PastAddress:
    End If
    Next j
End If
Next i
PastAddress:

'Mobile
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 3
    Temp = Mb(j)
        If VBA.Left(VBA.UCase(sigRow(i)), Len(Temp)) = Temp Then
        If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
        ActiveSheet.Range("Mobile") = VBA.Mid(sigRow(i), Len(Temp) + 2)
        Else
          If MsgBox("Mobile: " & VBA.Mid(sigRow(i), Len(Temp) + 2), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Mobile") = VBA.Mid(sigRow(i), Len(Temp) + 2)
        End If
    sigRow(i) = ""
    GoTo PastMobile:
    End If
    Next j
End If
Next i
PastMobile:

'Phone
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 1
    Temp = Ph(j)
        If VBA.Left(VBA.UCase(sigRow(i)), Len(Temp)) = Temp Then

            'TODO: Detect the intl or national extension here.. or if we can from the postcode.
            If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
            ActiveSheet.Range("Phone") = VBA.Mid(sigRow(i), Len(Temp) + 3)
            Else
              If MsgBox("Phone: " & VBA.Mid(sigRow(i), Len(Temp) + 3), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Phone") = VBA.Mid(sigRow(i), Len(Temp) + 3)
            End If

        sigRow(i) = ""
        GoTo PastPhone:
        End If
    Next j
End If
Next i
PastPhone:


'Email
For i = 1 To UBound(sigRow)
    If Len(sigRow(i)) > 0 Then
        'replace with regEx search
        If InStr(1, sigRow(i), "@", vbTextCompare) And InStr(1, VBA.UCase(sigRow(i)), ".CO", vbTextCompare) Then
        Dim email As String
        email = sigRow(i)
        email = Replace(VBA.UCase(email), "EMAIL:", "")
        email = Replace(VBA.UCase(email), "E-MAIL:", "")
        email = Replace(VBA.UCase(email), "E:", "")
        email = Replace(VBA.UCase(Trim(email)), "E ", "")
        email = VBA.LCase(email)

            If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
            ActiveSheet.Range("Email") = email
            Else
              If MsgBox("Email: " & email, vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Email") = email
            End If
        sigRow(i) = ""
        Exit For
        End If
    End If
Next i

'Now the only remaining items will be the postcode, suburb, country
'there shouldn't be any numbers (eg. from PoBox,Ph,Fax,Mobile) except for the Post Code

'Join the string and filter out the Post Code
Temp = Join(sigRow, vbCrLf)
Temp = Trim(Temp)

For i = 1 To Len(Temp)

Dim postCode As String
postCode = VBA.Mid(Temp, i, 4)

'In Australia PostCodes are 4 digits
If VBA.Mid(Temp, i, 1) <> " " And IsNumeric(postCode) Then

    If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
    ActiveSheet.Range("PostCode") = postCode
    Else
      If MsgBox("Post Code: " & postCode, vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("PostCode") = postCode
    End If

    'Lookup the Suburb and State based on the PostCode, the PostCode sheet has the lookup
    Dim mySuburbArray As Range
    Set mySuburbArray = Sheets("PostCodes").Range("A2:B16670")

    Dim suburbs As String
    For j = 1 To mySuburbArray.Columns(1).Cells.Count
    If mySuburbArray.Cells(j, 1) = postCode Then
        'Check if the suburb is listed in the address
        If InStr(1, UCase(Temp), mySuburbArray.Cells(j, 2), vbTextCompare) > 0 Then

        'Set the Suburb and State
        ActiveSheet.Range("Suburb") = mySuburbArray.Cells(j, 2)
        Stat = mySuburbArray.Cells(j, 3)
        ActiveSheet.Range("State") = Stat

        'Knowing the State - for Australia we can get the telephone Ext
        PhExt = PhExtension(VBA.UCase(Stat))
        ActiveSheet.Range("PhExt") = PhExt

        'remove the phone extension from the number
        Dim prePhone As String
        prePhone = ActiveSheet.Range("Phone")
        prePhone = Replace(prePhone, PhExt & " ", "")
        prePhone = Replace(prePhone, "(" & PhExt & ") ", "")
        prePhone = Replace(prePhone, "(" & PhExt & ")", "")
        ActiveSheet.Range("Phone") = prePhone
        Exit For
        End If
    End If
    Next j
Exit For
End If
Next i

End Sub


Private Function PhExtension(ByVal State As String) As String
Select Case State
Case Is = "NSW"
PhExtension = "02"
Case Is = "QLD"
PhExtension = "07"
Case Is = "VIC"
PhExtension = "03"
Case Is = "NT"
PhExtension = "04"
Case Is = "WA"
PhExtension = "05"
Case Is = "SA"
PhExtension = "07"
Case Is = "TAS"
PhExtension = "06"
End Select
End Function

Private Function Ph(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Ph = "PH"
Case Is = 1
Ph = "PHONE"
'Case Is = 2
'Ph = "P"
End Select
End Function

Private Function Mb(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Mb = "MB"
Case Is = 1
Mb = "MOB"
Case Is = 2
Mb = "CELL"
Case Is = 3
Mb = "MOBILE"
'Case Is = 4
'Mb = "M"
End Select
End Function

Private Function Fax(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Fax = "FAX"
Case Is = 1
Fax = "FACSIMILE"
'Case Is = 2
'Fax = "F"
End Select
End Function

Private Function State(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
State = "NSW"
Case Is = 1
State = "QLD"
Case Is = 2
State = "VIC"
Case Is = 3
State = "NT"
Case Is = 4
State = "WA"
Case Is = 5
State = "SA"
Case Is = 6
State = "TAS"
End Select
End Function

Private Function Street(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Street = " ST"
Case Is = 1
Street = " RD"
Case Is = 2
Street = " AVE"
Case Is = 3
Street = " AV"
Case Is = 4
Street = " CRES"
Case Is = 5
Street = " LOOP"
Case Is = 6
Street = "PO BOX"
Case Is = 7
Street = " STREET"
Case Is = 8
Street = " ROAD"
Case Is = 9
Street = " AVENUE"
Case Is = 10
Street = " CRESENT"
Case Is = 11
Street = " PARADE"
Case Is = 12
Street = " PDE"
Case Is = 13
Street = " LANE"
Case Is = 14
Street = " COURT"
Case Is = 15
Street = " BLVD"
Case Is = 16
Street = "P.O. BOX"
Case Is = 17
Street = "P.O BOX"
Case Is = 18
Street = "PO BOX"
Case Is = 19
Street = "POBOX"
End Select
End Function