Parse usable Dirección, ciudad, estado, código postal de una cadena

Problema: tengo un campo de dirección de una base de datos de Access que se ha convertido a Sql Server 2005. Este campo tiene todo en un solo campo. Necesito analizar las secciones individuales de la dirección en sus campos apropiados en una tabla normalizada. Necesito hacer esto para aproximadamente 4,000 registros y debe ser repetible.

Suposiciones

  1. Asumir una dirección en los EE. UU. (Por ahora)

  2. Supongamos que la cadena de entrada a veces contendrá un destinatario (la persona a la que se dirige) y / o una segunda dirección (es decir, Suite B)

  3. estados pueden ser abreviados

  4. El código postal puede ser estándar de 5 dígitos o zip + 4

  5. hay errores tipográficos en algunos casos

ACTUALIZACIÓN: En respuesta a las preguntas planteadas, los estándares no se siguieron universalmente, necesito almacenar los valores individuales, no solo geoencoding y errores significa error tipográfico (corregido arriba)

Data de muestra:

  • AP Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947

  • 11522 Shawnee Road, Greenwood DE 19950

  • 144 Kings Highway, SW Dover, DE 19901

  • Const. Servicios 2 Penns Way Suite 405 New Castle, DE 19720

  • Humes Realty 33 Bridle Ridge Court, Lewes, DE 19958

  • Excavación Nichols 2742 Pulaski Hwy Newark, DE 19711

  • 2284 Bryn Zion Road, Smyrna, DE 19904

  • VEI Dover Crossroads, LLC 1500 Serpentine Road, Suite 100 Baltimore MD 21

  • 580 North Dupont Highway Dover, DE 19901

  • PO Box 778 Dover, DE 19903

He trabajado mucho en este tipo de análisis. Debido a que hay errores, no obtendrá el 100% de precisión, pero hay algunas cosas que puede hacer para llegar la mayor parte del camino y luego hacer una prueba visual de BS. Esta es la manera general de hacerlo. No es código, porque es bastante académico escribirlo, no hay rarezas, solo mucha manipulación de cadenas.

(Ahora que ha publicado algunos datos de muestra, he hecho algunos cambios menores)

  1. Trabaja al revés. Comience desde el código postal, que estará cerca del final, y en uno de los dos formatos conocidos: XXXXX o XXXXX-XXXX. Si esto no aparece, puede suponer que está en la ciudad, en la parte de estado, a continuación.
  2. Lo siguiente, antes del zip, será el estado, y estará en formato de dos letras o como palabras. Tú también sabes cuáles serán estos: solo hay 50 de ellos. Además, podría soundexar las palabras para ayudar a compensar los errores ortográficos.
  3. antes esa es la ciudad, y probablemente esté en la misma línea que el estado. Puede usar una base de datos de código postal para verificar la ciudad y el estado en función del zip, o al menos usarlo como un detector de BS.
  4. La dirección de la calle generalmente será de una o dos líneas. La segunda línea generalmente será el número de la suite si hay una, pero también podría ser un apartado postal.
  5. Va a ser casi imposible detectar un nombre en la primera o segunda línea, aunque si no está prefijado con un número (o si está prefijado con un “attn” o “atención a:” podría darle una pista como a si es un nombre o una línea de dirección.

Espero que esto ayude un poco.

Creo que externalizar el problema es la mejor opción: enviarlo al geocodificador de Google (o Yahoo). El geocodificador devuelve no solo lat / long (que no son de interés aquí), sino también un rico análisis sintáctico de la dirección, con campos rellenos que no envió (incluidos ZIP + 4 y condado).

Por ejemplo, el rendimiento de “1600 Amphitheatre Parkway, Mountain View, CA” rinde

{ "name": "1600 Amphitheatre Parkway, Mountain View, CA, USA", "Status": { "code": 200, "request": "geocode" }, "Placemark": [ { "address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA", "AddressDetails": { "Country": { "CountryNameCode": "US", "AdministrativeArea": { "AdministrativeAreaName": "CA", "SubAdministrativeArea": { "SubAdministrativeAreaName": "Santa Clara", "Locality": { "LocalityName": "Mountain View", "Thoroughfare": { "ThoroughfareName": "1600 Amphitheatre Pkwy" }, "PostalCode": { "PostalCodeNumber": "94043" } } } } }, "Accuracy": 8 }, "Point": { "coordinates": [-122.083739, 37.423021, 0] } } ] } 

¡Ahora eso es parseable!

Es probable que el poster original haya pasado mucho tiempo, pero apunté a portar el Perl Geo :: StreetAddress: módulo de EE. UU. Utilizado por geocoder.us a C #, lo descargué en CodePlex y creo que las personas que se topen con esta pregunta en el futuro pueden encontrarlo útil:

Analizador de dirección de Estados Unidos

En la página principal del proyecto, bash hablar sobre sus (muy reales) limitaciones. Como no está respaldado por la base de datos de direcciones válidas de USPS, el análisis puede ser ambiguo y no puede confirmar ni negar la validez de una dirección determinada. Simplemente puede intentar sacar datos de la cadena.

Está destinado para el caso en que necesite obtener un conjunto de datos en su mayoría en los campos correctos, o desee proporcionar un acceso directo a la entrada de datos (permitiendo a los usuarios pegar una dirección en un cuadro de texto en lugar de tabular entre múltiples campos). No está destinado a verificar la capacidad de entrega de una dirección.

No intenta analizar nada por encima de la línea de la calle, pero uno probablemente podría lidiar con la expresión regular para obtener algo razonablemente cerca – Probablemente lo rompa en el número de la casa.

He hecho esto en el pasado.

O bien, hazlo manualmente (crea una buena interfaz gráfica de usuario que ayude al usuario a hacerlo rápidamente) o haz que se automatice y verifique contra una base de datos de direcciones reciente (tienes que comprar eso) y maneja los errores manualmente.

El manejo manual tomará alrededor de 10 segundos cada uno, lo que significa que puede hacer 3600/10 = 360 por hora, por lo que 4000 debería demorar aproximadamente 11-12 horas. Esto le dará una alta tasa de precisión.

Para la automatización, necesita una base de datos de direcciones de los EE. UU. Reciente y modifique sus reglas con eso. Sugiero que no te guste la expresión regular (difícil de mantener a largo plazo, con tantas excepciones). Realice una coincidencia del 90% con la base de datos, haga el rest manualmente.

Obtenga una copia de las normas de direccionamiento postal (USPS) en http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf y observe que tiene más de 130 páginas. Regexes para implementar eso sería una locura.

Para direcciones internacionales, todas las apuestas están desactivadas. Los trabajadores basados ​​en los EE. UU. No podrían validar.

Alternativamente, use un servicio de datos. Sin embargo, no tengo recomendaciones.

Además: cuando envía las cosas por correo (para eso sirve, ¿verdad?) Asegúrese de poner “corrección de dirección solicitada” en el sobre (en el lugar correcto) y actualice la base de datos. (Hicimos una guía simple para que la persona de la recepción lo hiciera, la persona que realmente ordena el correo)

Finalmente, cuando haya borrado datos, busque duplicados.

He estado trabajando en el dominio de procesamiento de dirección durante aproximadamente 5 años, y realmente no hay una bala de plata. La solución correcta dependerá del valor de los datos. Si no es muy valioso, inclúyalo a través de un analizador, como sugieren las otras respuestas. Si es incluso algo valioso, definitivamente necesitará que un ser humano evalúe / corrija todos los resultados del analizador sintáctico. Si está buscando una solución totalmente automática y repetible, probablemente quiera hablar con un proveedor de corrección de direcciones como Group1 o Trillium.

Después de los consejos aquí, he desarrollado la siguiente función en VB que crea aceptable, aunque no siempre perfecto (si se proporcionan un nombre de empresa y una línea de suite, combina los datos utilizables de la suite y la ciudad). Por favor, siéntete libre de comentar / refactor / gritarme por romper una de mis propias reglas, etc .:

 Public Function parseAddress(ByVal input As String) As Collection input = input.Replace(",", "") input = input.Replace(" ", " ") Dim splitString() As String = Split(input) Dim streetMarker() As String = New String() {"street", "st", "st.", "avenue", "ave", "ave.", "blvd", "blvd.", "highway", "hwy", "hwy.", "box", "road", "rd", "rd.", "lane", "ln", "ln.", "circle", "circ", "circ.", "court", "ct", "ct."} Dim address1 As String Dim address2 As String = "" Dim city As String Dim state As String Dim zip As String Dim streetMarkerIndex As Integer zip = splitString(splitString.Length - 1).ToString() state = splitString(splitString.Length - 2).ToString() streetMarkerIndex = getLastIndexOf(splitString, streetMarker) + 1 Dim sb As New StringBuilder For counter As Integer = streetMarkerIndex To splitString.Length - 3 sb.Append(splitString(counter) + " ") Next counter city = RTrim(sb.ToString()) Dim addressIndex As Integer = 0 For counter As Integer = 0 To streetMarkerIndex If IsNumeric(splitString(counter)) _ Or splitString(counter).ToString.ToLower = "po" _ Or splitString(counter).ToString().ToLower().Replace(".", "") = "po" Then addressIndex = counter Exit For End If Next counter sb = New StringBuilder For counter As Integer = addressIndex To streetMarkerIndex - 1 sb.Append(splitString(counter) + " ") Next counter address1 = RTrim(sb.ToString()) sb = New StringBuilder If addressIndex = 0 Then If splitString(splitString.Length - 2).ToString() <> splitString(streetMarkerIndex + 1) Then For counter As Integer = streetMarkerIndex To splitString.Length - 2 sb.Append(splitString(counter) + " ") Next counter End If Else For counter As Integer = 0 To addressIndex - 1 sb.Append(splitString(counter) + " ") Next counter End If address2 = RTrim(sb.ToString()) Dim output As New Collection output.Add(address1, "Address1") output.Add(address2, "Address2") output.Add(city, "City") output.Add(state, "State") output.Add(zip, "Zip") Return output End Function Private Function getLastIndexOf(ByVal sArray As String(), ByVal checkArray As String()) As Integer Dim sourceIndex As Integer = 0 Dim outputIndex As Integer = 0 For Each item As String In checkArray For Each source As String In sArray If source.ToLower = item.ToLower Then outputIndex = sourceIndex If item.ToLower = "box" Then outputIndex = outputIndex + 1 End If End If sourceIndex = sourceIndex + 1 Next sourceIndex = 0 Next Return outputIndex End Function 

Al pasar la función parseAddress “AP Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947”, se devuelve:

 2299 Lewes-Georgetown Hwy AP Croll & Son Georgetown DE 19947 

SmartyStreets tiene una nueva característica que extrae direcciones de cadenas de entrada arbitrarias. (Nota: no trabajo en SmartyStreets).

Se extrajeron con éxito todas las direcciones de la entrada de muestra dada en la pregunta anterior. (Por cierto, solo 9 de esas 10 direcciones son válidas).

Aquí hay algunos de los resultados: enter image description here

Y aquí está el resultado con formato CSV de esa misma solicitud:

 ID,Start,End,Segment,Verified,Candidate,Firm,FirstLine,SecondLine,LastLine,City,State,ZIPCode,County,DpvFootnotes,DeliveryPointBarcode,Active,Vacant,CMRA,MatchCode,Latitude,Longitude,Precision,RDI,RecordType,BuildingDefaultIndicator,CongressionalDistrict,Footnotes 1,32,79,"2299 Lewes-Georgetown Hwy, Georgetown, DE 19947",N,,,,,,,,,,,,,,,,,,,,,, 2,81,119,"11522 Shawnee Road, Greenwood DE 19950",Y,0,,11522 Shawnee Rd,,Greenwood DE 19950-5209,Greenwood,DE,19950,Sussex,AABB,199505209226,Y,N,N,Y,38.82865,-75.54907,Zip9,Residential,S,,AL,N# 3,121,160,"144 Kings Highway, SW Dover, DE 19901",Y,0,,144 Kings Hwy,,Dover DE 19901-7308,Dover,DE,19901,Kent,AABB,199017308444,Y,N,N,Y,39.16081,-75.52377,Zip9,Commercial,S,,AL,L# 4,190,232,"2 Penns Way Suite 405 New Castle, DE 19720",Y,0,,2 Penns Way Ste 405,,New Castle DE 19720-2407,New Castle,DE,19720,New Castle,AABB,197202407053,Y,N,N,Y,39.68332,-75.61043,Zip9,Commercial,H,,AL,N# 5,247,285,"33 Bridle Ridge Court, Lewes, DE 19958",Y,0,,33 Bridle Ridge Cir,,Lewes DE 19958-8961,Lewes,DE,19958,Sussex,AABB,199588961338,Y,N,N,Y,38.72749,-75.17055,Zip7,Residential,S,,AL,L# 6,306,339,"2742 Pulaski Hwy Newark, DE 19711",Y,0,,2742 Pulaski Hwy,,Newark DE 19702-3911,Newark,DE,19702,New Castle,AABB,197023911421,Y,N,N,Y,39.60328,-75.75869,Zip9,Commercial,S,,AL,A# 7,341,378,"2284 Bryn Zion Road, Smyrna, DE 19904",Y,0,,2284 Bryn Zion Rd,,Smyrna DE 19977-3895,Smyrna,DE,19977,Kent,AABB,199773895840,Y,N,N,Y,39.23937,-75.64065,Zip7,Residential,S,,AL,A#N# 8,406,450,"1500 Serpentine Road, Suite 100 Baltimore MD",Y,0,,1500 Serpentine Rd Ste 100,,Baltimore MD 21209-2034,Baltimore,MD,21209,Baltimore,AABB,212092034250,Y,N,N,Y,39.38194,-76.65856,Zip9,Commercial,H,,03,N# 9,455,495,"580 North Dupont Highway Dover, DE 19901",Y,0,,580 N DuPont Hwy,,Dover DE 19901-3961,Dover,DE,19901,Kent,AABB,199013961803,Y,N,N,Y,39.17576,-75.5241,Zip9,Commercial,S,,AL,N# 10,497,525,"PO Box 778 Dover, DE 19903",Y,0,,PO Box 778,,Dover DE 19903-0778,Dover,DE,19903,Kent,AABB,199030778781,Y,N,N,Y,39.20946,-75.57012,Zip5,Residential,P,,AL, 

Yo fui el desarrollador que originalmente escribió el servicio. El algoritmo que implementamos es un poco diferente de cualquier respuesta específica aquí, pero cada dirección extraída se verifica contra la API de búsqueda de direcciones, por lo que puede estar seguro si es válida o no. Cada resultado verificado está garantizado, pero sabemos que los otros resultados no serán perfectos porque, como se ha dejado muy claro en este hilo, las direcciones son impredecibles, incluso para los humanos a veces.

Esto no resolverá su problema, pero si solo necesitara datos lat / long para estas direcciones, la API de Google Maps analizará las direcciones no formateadas bastante bien.

Buena sugerencia, alternativamente, puede ejecutar una solicitud CURL para cada dirección en Google Maps y devolverá la dirección con el formato correcto. A partir de eso, puede express en regex al contenido de su corazón.

+1 a la solución sugerida por James A. Rosen, ya que me ha funcionado bien; sin embargo, para los completistas, este sitio es una lectura fascinante y el mejor bash que he visto de documentar direcciones en todo el mundo: http://www.columbia.edu/kermit /postal.html

¿Hay algún estándar en la forma en que se registran las direcciones? Por ejemplo:

  1. ¿Siempre hay comas o nuevas líneas que separan street1 de street2 de city of state from zip?
  2. ¿Los tipos de dirección (carretera, calle, bulevar, etc.) siempre están detallados? siempre abreviado? ¿Alguno de cada uno?
  3. Definir “error”.

Mi respuesta general es una serie de expresiones regulares, aunque la complejidad de esto depende de la respuesta. Y si no hay consistencia en absoluto, entonces solo podrá lograr el éxito parcial con un Regex (es decir, filtrando el código postal y el estado) y tendrá que hacer el rest a mano (o al menos pasar por el rest muy cuidadosamente para asegurarse de detectar los errores).

Otra solicitud de datos de muestra.

Como se ha mencionado, trabajaría al revés desde el zip.

Una vez que tenga un zip, consultaría una base de datos zip, almacenará los resultados y los eliminará y el zip de la cadena.

Eso te dejará con el desorden de la dirección. Las direcciones MOST (¿Todas?) Comenzarán con un número, de modo que busque la primera aparición de un número en la cadena restante y tome todo desde el final hasta el (nuevo) de la cadena. Esa será su dirección. Cualquier cosa a la izquierda de ese número es probablemente un destinatario.

Ahora debería tener la Ciudad, el Estado y el Zip almacenados en una tabla y posiblemente dos cadenas, destinatario y dirección. Para la dirección, verifique la existencia de “Suite” o “Apt.” etc. y divida eso en dos valores (líneas de dirección 1 y 2).

Para el destinatario puntuaría y tomaría la última palabra de esa cadena como el apellido y pondría el rest en el campo del primer nombre. Si no quiere hacer eso, deberá verificar el saludo (Sr., Sra., Dr., etc.) al principio y hacer algunas suposiciones basadas en el número de espacios en cuanto a cómo se llama el nombre. arreglado.

No creo que haya manera de analizar con 100% de precisión.

Pruebe http://www.address-parser.com . Usamos su servicio web, que puedes probar en línea

En base a los datos de muestra:

  1. Comenzaría al final de la cadena. Parse un código postal (cualquier formato). Lea el final del primer espacio. Si no se encontró ningún código postal Error.

  2. Recorta el extremo para espacios y caracteres especiales (comas)

  3. Luego avance al estado, nuevamente use el espacio como el delimitador. Tal vez use una lista de búsqueda para validar códigos de estado de 2 letras y nombres completos de estado. Si no se encuentra un estado válido, error.

  4. Recorta espacios y comas desde el final nuevamente.

  5. La ciudad se vuelve complicada, realmente usaría una coma aquí, a riesgo de obtener demasiados datos en la ciudad. Busque la coma o el comienzo de la línea.

  6. Si todavía tiene caracteres en la cadena, inserte todo eso en un campo de dirección.

Esto no es perfecto, pero debería ser un buen punto de partida.

Si se trata de datos ingresados ​​por humanos, pasarás demasiado tiempo intentando codificar las excepciones.

Tratar:

  1. Expresión regular para extraer el código postal

  2. Búsqueda de código postal (a través del DB del gobierno apropiado) para obtener la dirección correcta

  3. Obtener un pasante para verificar manualmente que los datos nuevos coinciden con el anterior

Esto no resolverá su problema, pero si solo necesitara datos lat / long para estas direcciones, la API de Google Maps analizará las direcciones no formateadas bastante bien.

RecogniContact es un objeto COM de Windows que analiza direcciones estadounidenses y europeas. Puede intentarlo directamente en http://www.loquisoft.com/index.php?page=8

Es posible que desee comprobar esto! http://jgeocoder.sourceforge.net/parser.html Funcionó como un encanto para mí.

Este tipo de problema es difícil de resolver debido a las ambigüedades subyacentes en los datos.

Aquí hay una solución basada en Perl que define un árbol de gramática de descenso recursivo basado en expresiones regulares para analizar muchas combinaciones válidas de direcciones de calles: http://search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua /EN/AddressParse.pm . Esto incluye sub propiedades dentro de una dirección como: 12 1st Avenue N Suite # 2 Somewhere CA 12345 USA

Es similar a http://search.cpan.org/~timb/Geo-StreetAddress-US-1.03/US.pm mencionado anteriormente, pero también funciona para direcciones que no son de EE. UU., Como el Reino Unido, Australia y Canadá.

Aquí está el resultado de una de sus direcciones de muestra. Tenga en cuenta que la sección de nombre debería eliminarse primero de “AP Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947” para reducirla a “2299 Lewes-Georgetown Hwy, Georgetown, DE 19947”. Esto se logra fácilmente eliminando todos los datos hasta el primer número encontrado en la cadena.

 Non matching part '' Error '0' Error descriptions '' Case all '2299 Lewes-Georgetown Hwy Georgetown DE 19947' COMPONENTS '' country '' po_box_type '' post_box '' post_code '19947' pre_cursor '' property_identifier '2299' property_name '' road_box '' street 'Lewes-Georgetown' street_direction '' street_type 'Hwy' sub_property_identifier '' subcountry 'DE' suburb 'Georgetown' 

Dado que existe la posibilidad de error en la palabra, piense en usar SOUNDEX combinado con el algoritmo LCS para comparar cadenas, ¡esto ayudará mucho!

usando la API de google

 $d=str_replace(" ", "+", $address_url); $completeurl ="http://maps.googleapis.com/maps/api/geocode/xml?address=".$d."&sensor=true"; $phpobject = simplexml_load_file($completeurl); print_r($phpobject); 

Para los desarrolladores de Ruby o Rails hay una buena gem disponible llamada street_address . He estado usando esto en uno de mis proyectos y hace el trabajo que necesito.

El único problema que tuve fue que cada vez que una dirección tenía este formato, PO Box 1410 Durham, NC 27702 devolvió cero y, por lo tanto, tuve que reemplazar “PO Box” por “” y después de esto, pude analizarlo.

Hay servicios de datos que, dado un código postal, le darán una lista de nombres de calles en ese código postal.

Use una expresión regular para extraer Zip o City State – encuentre la correcta o si un error obtiene ambas. saque la lista de calles de una fuente de datos Corrija la ciudad y el estado, y luego la dirección de la calle. Una vez que obtenga una línea de dirección válida 1, ciudad, estado y código postal, puede hacer suposiciones en la línea de dirección 2..3

No sé CÓMO ES POSIBLE, pero no lo he mencionado, así que pensé que podría seguir y sugerir esto:

Si está estrictamente en los EE. UU .. obtenga una gran base de datos de todos los códigos postales, estados, ciudades y calles. Ahora busca esto en tus direcciones. Puede validar lo que encuentra al probar si, por ejemplo, la ciudad que encontró existe en el estado que encontró, o al verificar si la calle que encontró existe en la ciudad que encontró. Si no, es probable que John no sea por la calle de John, sino que es el nombre del destinatario … Básicamente, obtenga la mayor cantidad de información posible y compruebe sus direcciones en su contra. Un ejemplo extremo sería obtener UNA LISTA DE TODAS LAS DIRECCIONES EN LOS EE. UU. DE A y luego encontrar cuál tiene la coincidencia más relevante con cada una de sus direcciones …

Existe un puerto javascript de perl Geo :: StreetAddress :: paquete US: https://github.com/hassansin/parse-address . Está basado en expresiones regulares y funciona bastante bien.