2012-05-29 3 views
6

Sto provando a scrivere una funzione VBA in Access che sostituisce le parole in un campo indirizzo con lo standard United States Postal Abbreviations. Mi rendo conto che questo non sarà mai perfetto, ma voglio almeno fare abbreviazioni semplici (senza dover acquistare software di formattazione degli indirizzi), ad es.Utilizzo di RegEx e Sostituisci per aggiornare i campi indirizzo con le abbreviazioni USPS in MS-Access

input  output 
------- ------------- 
North -> N 
Street -> ST 
Drive -> DR 
Lane -> LN 

ho pensato di utilizzare una semplice tabella per memorizzare la stringa e la stringa di sostituzione, e quindi scorrendo tale tabella/recordset per eseguire una semplice ricerca e sostituzione utilizzando la funzione Replace(), esempio base alle immediate window:

?Replace("123 North 3rd St", "North", "N", compare:=vbTextCompare) 
123 N 3rd St 

Tuttavia, questo metodo può potenzialmente causare errori, ad esempio

?Replace("123 Northampton St", "North", "N", compare:=vbTextCompare) 
123 Nampton St 

La mia strategia iniziale era quello di creare una tabella di sostituzione con modelli di espressioni regolari e stringhe di sostituzione, quindi collegare attraverso quel tavolo per fare una ricerca più precisa e sostituire.

pattern     abbrev 
-------------------  ------------ 
{pattern for North}  N 
{pattern for Street} ST 

ho capito che RegEx potrebbe essere eccessivo qui, soprattutto perché ho intenzione di essere scorrendo campi indirizzo più e più volte in un database, ma non riusciva a pensare a un modo più semplice usando solo la funzione Replace() (Aggiornamento: vedere le risposte da @ mwolfe02 e @Cylian e una soluzione ibrida).

Nell'esempio precedente, voglio cercare le parole Nord e Street quando sono o come una parola in una stringa (separate da due spazi bianchi) o alla fine della stringa o all'inizio di una stringa. Questo copre la maggior parte delle situazioni che giustificano un'abbreviazione. ad es.

address      formatted 
----------------------  -------------------------- 
123 North 3rd St   -> 123 N 3RD ST 
123 ABC Street North  -> 123 ABC ST N 
North 3rd Street   -> N 3RD ST 
123 North Northampton St -> 123 N NORTHAMPTON ST 

come in questi esempi, voglio sostituire tutte le istanze del modello nella stringa. Inoltre sto convertendo tutto in maiuscolo (posso usare UCase() sul risultato finale senza problemi).

Qualcuno sa di un modulo esistente che fa questo genere di cose? Qualcuno può aiutare con il modello di corrispondenza come negli esempi di cui sopra? Per ulteriore credito, sono curioso anche di creare una regola nella tabella per la formattazione delle caselle postali, ad es.

address     formatted 
---------------------- -------------------------- 
P.O. Box 345   -> PO BOX 345 
PO Box 345    -> PO BOX 345 
Post Office Box 345 -> PO BOX 345 
PO. Box 345   -> PO BOX 345 
P. O. Box 345   -> PO BOX 345 

This stack overflow post ha pronunciato la seguente modello di riconoscere alcune caselle postali "^ \ s * P.? \ S? O.? \ SB [Oo] [xx]." (certamente non il terzo esempio sopra). Ancora una volta, non mi sento a mio agio con i set di adattamento e sostituzione per capire come scrivere questa funzione di sostituzione più precisa. Esiste un esperto in RegEx/Access che può aiutare?

risposta

0

ho creato una semplice tabella di riferimento ref_USPS_abbrev dalla lista Abbreviazione USPS on-line. Ecco le voci che corrispondono all'esempio originariamente data:

WORD   ABBREV 
------------ ------------- 
NORTH   N 
STREET  ST 

Poi, incorporando le risposte al mio post originale, ho creato due funzioni di supporto.

Da @Cylian:

' ----------------------------------------------------------------------' 
    ' Formats string containing P.O. Box to USPS Approved PO BOX format ' 
    ' ----------------------------------------------------------------------' 
    ' Requires Microsoft VBScript Regular Expressions 5.5 

    Public Function FormatPO(inputString As String) As String 

     Static rePO As Object 
     If rePO Is Nothing Then 
      Set rePO = CreateObject("vbscript.regexp") 
     With rePO 
     .Pattern = "\bP(?:[. ]+|ost +)?O(?:ff\.?(?:ice))" & _ 
        "?[. ]+B(?:ox|\.) +(\d+)\b" 
     .Global = True 
     .IgnoreCase = True 
     End With 
     End If 

     With rePO 
      If .Test(inputString) Then 
       FormatPO = .Replace(inputString, "PO BOX $1") 
      Else 
       FormatPO = inputString 
      End If 
     End With 
    End Function 

E, con ottima idea @ di mwolfe02:

' ----------------------------------------------------------------------' 
    ' Replaces whole word only with an abbreviation in address string  ' 
    ' ----------------------------------------------------------------------' 

    Public Function AddressReplace(AddressLine As String, _ 
        FullName As String, _ 
        Abbrev As String) 

    'Enclose address line in an opening and closing space, so that you 
    'can require an opening and closing space on each word you are trying 
    'to replace. Finish up with a trim to get rid of those temporary spaces. 

    AddressReplace = Trim(Replace(" " & AddressLine & " ", _ 
           " " & FullName & " ", _ 
           " " & Abbrev & " ")) 
    End Function 

Poi, incorporando queste funzioni di aiuto, ho scritto questa funzione:

' ----------------------------------------------------------------------' 
' Format address using abbreviations stored in table ref_USPS_abbrev ' 
' ----------------------------------------------------------------------' 
' Requires Microsoft DAO 3.6 Object Library 
' Table ref_USPS_abbrev has two fields: WORD (containing the word to match) 
' and ABBREV containing the desired abbreviated substitution. 
' United States Postal Services abbreviations are available at: 
' https://www.usps.com/ship/official-abbreviations.htm 

Public Function SubstituteUSPS(address As String) As String 

Static dba As DAO.Database 
Static rst_abbrev As DAO.Recordset 

    If IsNull(address) Then Exit Function 

    'Initialize the objects 

    If dba Is Nothing Then 
     Set dba = CurrentDb 
    End If 

    'Create the rst_abbrev recordset once from ref_USPS_abbrev. If additional 
    'entries are added to the source ref_USPS_abbrev table after the recordset 
    'is created, since it is an dbOpenTable (by default), the recordset will 
    'be updated dynamically. If you use dbOpenSnapshot it will not update 
    'dynamically. 

    If rst_abbrev Is Nothing Then 
     Set rst_abbrev = dba.OpenRecordset("ref_USPS_abbrev", _ 
              Type:=dbOpenTable) 
    End If 

    'Since rst_abbrev is a static object, in the event the function is called 
    'in succession (e.g. while looping through a recordset to update values), 
    'move to the first entry in the recordset each time the function is 
    'called. 

    rst_abbrev.MoveFirst 

    'Only call the FormatPO helper function if the address has the 
    'string "ox" in it.  

    If InStr(address, "ox") > 0 Then 
     address = FormatPO(address) 
    End If 

    'Loop through the recordset containing the abbreviations 
    'and use the AddressReplace helper function to substitute 
    'abbreviations for whole words only. 

    Do Until rst_abbrev.EOF 
     address = AddressReplace(address, rst_abbrev![WORD], _ 
           rst_abbrev![ABBREV]) 
     rst_abbrev.MoveNext 
    Loop 

    'Convert the address to upper case and trim white spaces and return result 
    'You can also add code here to trim out punctuation in the address, too. 

    SubstituteUSPS = Trim(UCase(address)) 

End Function 

Per creare la tabella ref_USPS_abbrev per il test:

Sub CreateUSPSTable() 

Dim dbs As Database 
Set dbs = CurrentDb 

With dbs 
    .Execute "CREATE TABLE ref_USPS_abbrev " _ 
     & "(WORD CHAR, ABBREV CHAR);" 
    .Execute " INSERT INTO ref_USPS_abbrev " _ 
     & "(WORD, ABBREV) VALUES " _ 
     & "('NORTH', 'N');" 
    .Execute " INSERT INTO ref_USPS_abbrev " _ 
     & "(WORD, ABBREV) VALUES " _ 
     & "('STREET', 'ST');" 
    .Close 
End With 
End Sub 

Infine, prove della funzione dal immediate window:

CreateUSPSTable 
?SubstituteUSPS("Post Office Box 345 123 North Northampton Street") 
PO BOX 345 123 N NORTHAMPTON ST 

Io non sono un programmatore professionale, quindi mi piacerebbe benvenuto suggerimenti per ripulire il mio codice ancora di più, ma per ora questo funziona grande. Grazie a tutti.

Stack Overflow ancora FTW!

+0

Al momento, sto riscontrando problemi nell'usare la funzione '' SubstituteUSPS() '' mentre faccio il giro della mia tabella sorgente. Non ho problemi con la funzione quando usato da solo; ma non sembra funzionare se combinato con un altro recordset. Idee? – regulus

+0

L'ho capito. Dopo ogni passaggio, ho bisogno di ripristinare il recordset statico ** rst_abbrev ** nella prima posizione. Ho aggiornato il codice. – regulus

5

Prova questa funzione

Public Function FormatPO(inputString$) 
'This example uses **Microsoft VBScript Regular Expressions 5.5** 
Dim re As New RegExp, result$ 
With re 
    .Pattern = "\bP(?:[. ]+|ost +)?O(?:ff\.?(?:ice))?[. ]+B(?:ox|\.) +(\d+)\b" 
    .Global = True 
    .IgnoreCase = True 
    If .test(inputString) Then 
     FormatPO = .Replace(inputString, "PO BOX $1") 
    Else 
     MsgBox "Data doesn't matched!" 
    End If 
End With 

e potrebbe essere chiamato come (da immediate window)

?FormatPO("P.O. Box 563") 

dà risultato

PO BOX 563 

Il modello di corrispondenza per i nomi di strade con indirizzi richiede più tempo per essere costruito. Ma puoi visitare here e creare il tuo RegEx online.

Spero che questo aiuti.

+0

Grazie per il vostro aiuto con il modello PO Box. Qualsiasi possibilità di persuaderti a utilizzare le tue abilità RegExp su [il mio altro post sull'estrazione dei numeri di autorizzazione] (http://stackoverflow.com/questions/10799500/regex-pattern-to-extract-authorization- numbers). Ho provato a utilizzare [GFSkinner's Tool] (http://gskinner.com/RegExr?) Che hai consigliato con un discreto successo, ma ho ancora le ruote di addestramento RegExp! Grazie! – regulus

+1

Prego. Ho aggiunto una soluzione al post. Spero che questo ti aiuti. – Cylian

2

@Cylian ha una buona risposta per la seconda parte della domanda. Proverò ad affrontare il primo. Se la vostra unica preoccupazione è che si sostituiscono le parole intere nell'indirizzo allora la seguente funzione farà quello che vi serve:

Function AddressReplace(AddressLine As String, _ 
         FullName As String, _ 
         Abbrev As String) 
    AddressReplace = Trim(Replace(" " & AddressLine & " ", _ 
            " " & FullName & " ", _ 
            " " & Abbrev & " ")) 
End Function 

Esso racchiude la linea di indirizzo in uno spazio di apertura e chiusura, in modo da poter richiedere un'apertura e chiudere lo spazio su ogni parola che stai cercando di sostituire. Finisce con un assetto per sbarazzarsi di quegli spazi temporanei.

La procedura che segue mette alla prova il codice e produce l'uscita che si sta cercando:

Sub TestAddressReplace() 
    Debug.Print AddressReplace("123 North 3rd St", "North", "N") 
    Debug.Print AddressReplace("123 Northampton St", "North", "N") 
End Sub 
+1

+1, Bel pezzo di codifica (senza RegEx.) – Cylian

2

L'USPS dispone di un'API di ricerca gratuita per convalidare e standardizzare gli indirizzi. Dovrai registrarti per il servizio (veloce) e quindi utilizzare l'id/password nell'API per rimbalzare contro il loro sito. Fa tutto il lavoro per te e ha un codice di esempio. Il servizio postale canadese ha la stessa cosa (non è sicuro che sia gratuito).

https://www.usps.com/business/web-tools-apis/welcome.htm

B. Sevier