web-dev-qa-db-fra.com

Comment analyser une adresse postale/postale libre avec du texte et des composants

Nous travaillons principalement aux États-Unis et nous essayons d’améliorer l’expérience utilisateur en combinant tous les champs d’adresse dans un seul et même champ de texte. Mais il y a quelques problèmes:

  • L'adresse que l'utilisateur type peut ne pas être correcte ou dans un format standard
  • L'adresse doit être séparée en parties (rue, ville, état, etc.) pour traiter les paiements par carte de crédit.
  • Les utilisateurs peuvent entrer plus que leur adresse (comme leur nom ou leur entreprise)
  • Google peut le faire, mais les conditions d'utilisation et les limites des requêtes sont prohibitives, en particulier avec un budget serré.

Apparemment, c'est une question commune:

Y a-t-il un moyen d'isoler une adresse du texte qui l'entoure et de la casser en morceaux? Existe-t-il une expression régulière pour analyser les adresses?

108
Matt

J'ai souvent vu cette question lorsque je travaillais pour une entreprise de vérification d'adresses. Je poste la réponse ici pour la rendre plus accessible aux programmeurs qui cherchent la même question. La société à laquelle j’étais chez nous a traité des milliards d’adresses et nous avons beaucoup appris au cours du processus.

Premièrement, nous devons comprendre quelques points concernant les adresses.

Les adresses ne sont pas normal

Cela signifie que les expressions régulières sont supprimées. J'ai tout vu, des expressions régulières simples qui font correspondre les adresses dans un format très spécifique, à ceci:

/\s + (\ d {2,5}\s +) (?! [a | p] m\b) (([a-zA-Z |\s +] {1,5}) {1,2}) ? ([\ s | \, |.] +)? (([a-zA-Z |\s +] {1,30}) {1,4}) (cour | 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 | OU | PA | RI | SC | SD | TN | TX | UT | VA | VI | VT | WA | WI | WV | WY) ([\ s | \, |.] +)? (\ S +\d {5})? ([\ S | \, |.] +)/i

... to this où un fichier de classe de classe 900+ génère une expression régulière supermassive à la volée pour correspondre encore plus. Je ne les recommande pas (par exemple, voici un violon de la regex ci-dessus, qui fait beaucoup d'erreurs ). Il n’existe pas de formule magique simple pour que cela fonctionne. En théorie et en théorie, il n'est pas possible de faire correspondre les adresses à une expression régulière.

SPS Publication 28 documente les nombreux formats d’adresses possibles, avec tous leurs mots-clés et variantes. Pire encore, les adresses sont souvent ambiguës. Les mots peuvent signifier plus d'une chose ("St" peut être "Saint" ou "Rue") et il y a des mots que je suis sûr qu'ils ont inventés. (Qui savait que "Stravenue" était un suffixe de rue?)

Vous aurez besoin d'un code qui comprend vraiment les adresses et si ce code existe, il s'agit d'un secret commercial. Mais vous pourriez probablement lancer le vôtre si vous y tenez vraiment.

Les adresses sont de formes et de tailles inattendues

Voici quelques adresses artificielles (mais complètes):

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Même ceux-ci sont éventuellement valables:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

Évidemment, ceux-ci ne sont pas normalisés. La ponctuation et les sauts de ligne ne sont pas garantis. Voici ce qui se passe:

  1. Le numéro 1 est complet car il contient une adresse postale, une ville et un état. Avec cette information, il suffit d’identifier l’adresse et elle peut être considérée comme "livrable" (avec une certaine normalisation).

  2. Le numéro 2 est complet car il contient également une adresse municipale (avec numéro secondaire/numéro d'unité) et un code postal à 5 ​​chiffres, ce qui suffit pour identifier adresse.

  3. Le numéro 3 est un format de boîte postale complet, car il contient un code postal.

  4. Le numéro 4 est également complet car le code postal est unique , ce qui signifie qu'une entité privée ou une société a acheté cet espace adresse. Un code postal unique est destiné aux espaces de livraison volumineux ou concentrés. Tout ce qui est adressé au code postal 12345 va à General Electric à Schenectady, NY. Cet exemple n'atteindra personne en particulier, mais l'USPS pourra toujours le livrer.

  5. Le numéro 5 est également complet, croyez-le ou non. Avec seulement ces chiffres, l'adresse complète peut être découverte lors de l'analyse avec une base de données de toutes les adresses possibles. Remplir les directions manquantes, l'indicatif secondaire et le code Zip + 4 est trivial lorsque vous voyez chaque nombre comme un composant. Voici à quoi cela ressemble, entièrement développé et normalisé:

205 N 1105 W App 14

Beverly Hills CA 90210-5221

Les données d'adresse ne vous appartiennent pas

Dans la plupart des pays qui fournissent des adresses officielles aux vendeurs autorisés, les adresses mêmes appartiennent à l’organisme directeur. Aux États-Unis, l'USPS est propriétaire des adresses. Il en va de même pour Postes Canada, la Royal Mail et d’autres, bien que chaque pays impose ou définisse la propriété un peu différemment. Sachant cela est important, car cela interdit généralement le reverse engineering de la base de données d'adresses. Vous devez faire attention à la manière d’acquérir, de stocker et d’utiliser les données.

Google Maps est un moyen courant de corriger rapidement les adresses, mais le TOS est plutôt prohibitif; Par exemple, vous ne pouvez pas utiliser leurs données ou API sans afficher Google Map, et à des fins non commerciales uniquement (sauf si vous payez), et vous ne pouvez pas stocker les données (sauf pour la mise en cache temporaire). Logique. Les données de Google sont parmi les meilleures au monde. Cependant, Google Maps ne vérifie pas l'adresse. Si une adresse n'existe pas, elle vous indiquera quand même l'adresse serait si elle existait (essayez-la sur votre votre propre rue; utilisez un numéro de rue dont vous savez qu’il n’existe pas). C'est utile parfois, mais soyez conscient de cela.

La politique de Nominatim politique d'utilisation est également restrictive, en particulier pour les gros volumes et les utilisations commerciales. Les données sont principalement extraites de sources gratuites. Elles ne sont donc pas aussi bien conservées (telle est la nature des projets ouverts) - - Toutefois, cela peut toujours répondre à vos besoins. Il est soutenu par une grande communauté.

L'USPS lui-même a une API, mais il tombe beaucoup et est livré sans garantie ni support. Il pourrait également être difficile à utiliser. Certaines personnes l'utilisent avec parcimonie, sans problèmes. Mais il est facile de rater le fait que l'USPS exige que vous utilisiez leur API uniquement pour confirmer les adresses à expédier par leur intermédiaire.

Les gens s'attendent à ce que les adresses soient difficiles

Malheureusement, nous avons conditionné notre société à attendre des adresses compliquées. Il existe des dizaines de bons articles UX sur Internet, mais le fait est que, si vous avez un formulaire d'adresse avec des champs individuels, c'est ce à quoi les utilisateurs s'attendent, même si cela complique la tâche des adresses de cas Edge qui ne correspondent pas à la formater le formulaire attendu, ou peut-être le formulaire nécessite-t-il un champ qu'il ne devrait pas. Ou les utilisateurs ne savent pas où mettre une certaine partie de leur adresse.

Je pourrais continuer encore et encore à propos de la mauvaise UX des formulaires de commande ces jours-ci, mais je dirai plutôt que combiner les adresses dans un seul champ sera un changement bienvenu - les gens pourront taper leur adresse comme bon leur semble, plutôt que d'essayer de comprendre votre long formulaire. Cependant, cette modification sera inattendue et les utilisateurs pourront la trouver un peu choquante au début. Soyez conscient de cela.

Une partie de cette douleur peut être soulagée en mettant le champ de pays à l'avant, avant l'adresse. Quand ils remplissent d'abord le champ du pays, vous savez comment faire apparaître votre formulaire. Vous avez peut-être un bon moyen de gérer les adresses américaines à champ unique. Ainsi, s'ils sélectionnent les États-Unis, vous pouvez réduire votre formulaire à un seul champ, sinon afficher les champs de composant. Juste des choses à penser!

Maintenant nous savons pourquoi c'est difficile; Que peux-tu y faire?

Les fournisseurs de licences USPS utilisent un processus appelé certification CASS ™ pour fournir des adresses vérifiées aux clients. Ces fournisseurs ont accès à la base de données USPS, mise à jour mensuellement. Leur logiciel doit être conforme à des normes rigoureuses pour être certifié, et il n’est pas souvent nécessaire d’accepter les conditions restrictives décrites ci-dessus.

De nombreuses entreprises certifiées CASS peuvent traiter des listes ou avoir des API: Melissa Data, Experian QAS et SmartyStreets, pour n'en nommer que quelques-unes.

(En raison de la flak pour "publicité", j'ai tronqué ma réponse à ce stade. C'est à vous de trouver une solution qui vous convient.)

La vérité: Vraiment, je ne travaille dans aucune de ces entreprises. Ce n'est pas une publicité.

256
Matt

libpostal: une bibliothèque open-source pour analyser les adresses, apprendre à utiliser les données d'OpenStreetMap, OpenAddresses et OpenCage.

https://github.com/openvenues/libpostal ( plus d'informations à ce sujet )

Autres outils/services:

10
David Portabella

Il existe de nombreux analyseurs d'adresses de rue. Ils viennent dans deux types de base - ceux qui ont des bases de données de noms de lieux et de noms de rues et ceux qui n'en ont pas. 

Un analyseur d’adresses de rue à expression régulière peut obtenir un taux de réussite d’environ 95% sans trop de peine. Ensuite, vous commencez à frapper les cas inhabituels. Celui de Perl dans CPAN, "Geo :: StreetAddress :: US", est à peu près aussi bon. Il y a des ports Python et Javascript, tous open source. J'ai une version améliorée en Python qui augmente légèrement le taux de réussite en traitant plus de cas. Pour que les 3% restants soient corrects, vous avez besoin de bases de données qui aident à lever l’ambiguïté. 

Une base de données avec des codes postaux à 3 chiffres, des noms d’États américains et des abréviations est d’une grande aide. Lorsqu'un analyseur voit un code postal et un nom d'état cohérents, il peut commencer à se verrouiller sur le format. Cela fonctionne très bien pour les États-Unis et le Royaume-Uni. 

Une analyse correcte des adresses de rue commence à la fin et fonctionne à l'envers. C'est comme ça que les systèmes USPS le font. Les adresses sont moins ambiguës à la fin, où les noms de pays, les noms de villes et les codes postaux sont relativement faciles à reconnaître. Les noms de rue peuvent généralement être isolés. Les emplacements dans les rues sont les plus complexes à analyser; vous rencontrez des choses comme "Fifth Floor" et "Staples Pavillion". C'est alors qu'une base de données est d'une grande aide. 

10
John Nagle

UPDATE: Geocode.xyz fonctionne maintenant dans le monde entier. Pour des exemples, voir https://geocode.xyz

Pour les États-Unis, le Mexique et le Canada, voir geocoder.ca .

Par exemple:

Entrée: quelque chose se passe près de l'intersection de main et d'arthur kill new york

Sortie:

<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>

Vous pouvez également vérifier les résultats dans l'interface Web ou obtenir une sortie au format Json ou Jsonp. par exemple. Je cherche des restaurants autour du 123 Main Street, New York

8
Ervin Ruci

Pour l'analyse d'adresses aux États-Unis,

Je préfère utiliser le paquet usaddress disponible dans pip pour l'adresse usd uniquement

python3 -m pip install usaddress

Documentation
PyPi

Cela a bien fonctionné pour moi pour l’adresse américaine.

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

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

if __== '__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] + '\n')
        print(dumps({}))
    else:
        print(dumps(dict(address)))

Exécuter le fichier 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"}
1
theBuzzyCoder

Pas de code? Pour la honte!

Voici un analyseur d'adresses JavaScript simple. C'est assez horrible pour chaque raison donnée par Matt dans sa thèse ci-dessus (avec laquelle je suis presque entièrement d'accord: les adresses sont des types complexes, et les humains font des erreurs; il est préférable d'externaliser et d'automatiser cela - quand vous pouvez vous le permettre).

Mais plutôt que de pleurer, j'ai décidé d'essayer:

Ce code fonctionne bien pour l’analyse de la plupart des résultats Esri pour findAddressCandidate, ainsi que pour certains autres géocodeurs (inversés) qui renvoient une adresse sur une seule ligne où la rue/la ville/l’état sont délimités par des virgules. Vous pouvez étendre si vous le souhaitez ou écrire des analyseurs syntaxiques spécifiques à un pays. Ou utilisez simplement ceci comme étude de cas montrant à quel point cet exercice peut être difficile ou à quel point je suis moche à JavaScript. J'admets que je n'ai passé qu'une trentaine de minutes à ce sujet (les itérations futures pourraient ajouter des caches, une validation Zip, des recherches d'état ainsi qu'un contexte d'emplacement utilisateur), mais cela a fonctionné pour mon cas d'utilisation: l'utilisateur final voit le formulaire qui analyse la réponse de recherche de géocodage en 4 zones de texte. Si l'analyse des adresses est mauvaise (ce qui est rare si les données source ne sont pas médiocres), ce n'est pas grave, l'utilisateur peut vérifier et corriger le problème! (Mais pour les solutions automatisées, vous pouvez ignorer/ignorer ou signaler comme erreur afin que dev puisse prendre en charge le nouveau format ou réparer les données source.)

/* 
address assumptions:
- US addresses only (probably want separate parser for different countries)
- No country code expected.
- if last token is a number it is probably a postal code
-- 5 digit number means more likely
- if last token is a hyphenated string it might be a postal code
-- if both sides are numeric, and in form #####-#### it is more likely
- if city is supplied, state will also be supplied (city names not unique)
- Zip/postal code may be omitted even if has city & state
- state may be two-char code or may be full state name.
- commas: 
-- last comma is usually city/state separator
-- second-to-last comma is possibly street/city separator
-- other commas are building-specific stuff that I don't care about right now.
- token count:
-- because units, street names, and city names may contain spaces token count highly variable.
-- simplest address has at least two tokens: 714 Oak
-- common simple address has at least four tokens: 714 S Oak ST
-- common full (mailing) address has at least 5-7:
--- 714 Oak, RUMTOWN, VA 59201
--- 714 S Oak ST, RUMTOWN, VA 59201
-- complex address may have a dozen or more:
--- MAGICICIAN SUPPLY, LLC, UNIT 213A, MAGIC TOWN MALL, 13 MAGIC CIRCLE DRIVE, LAND OF MAGIC, MA 73122-3412
*/

var rawtext = $("textarea").val();
var rawlist = rawtext.split("\n");

function ParseAddressEsri(singleLineaddressString) {
  var address = {
    street: "",
    city: "",
    state: "",
    postalCode: ""
  };

  // tokenize by space (retain commas in tokens)
  var tokens = singleLineaddressString.split(/[\s]+/);
  var tokenCount = tokens.length;
  var lastToken = tokens.pop();
  if (
    // if numeric assume postal code (ignore length, for now)
    !isNaN(lastToken) ||
    // if hyphenated assume long Zip code, ignore whether numeric, for now
    lastToken.split("-").length - 1 === 1) {
    address.postalCode = lastToken;
    lastToken = tokens.pop();
  }

  if (lastToken && isNaN(lastToken)) {
    if (address.postalCode.length && lastToken.length === 2) {
      // assume state/province code ONLY if had postal code
      // otherwise it could be a simple address like "714 S Oak ST"
      // where "ST" for "street" looks like two-letter state code
      // possibly this could be resolved with registry of known state codes, but meh. (and may collide anyway)
      address.state = lastToken;
      lastToken = tokens.pop();
    }
    if (address.state.length === 0) {
      // check for special case: might have State name instead of State Code.
      var stateNameParts = [lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken];

      // check remaining tokens from right-to-left for the first comma
      while (2 + 2 != 5) {
        lastToken = tokens.pop();
        if (!lastToken) break;
        else if (lastToken.endsWith(",")) {
          // found separator, ignore stuff on left side
          tokens.Push(lastToken); // put it back
          break;
        } else {
          stateNameParts.unshift(lastToken);
        }
      }
      address.state = stateNameParts.join(' ');
      lastToken = tokens.pop();
    }
  }

  if (lastToken) {
    // here is where it gets trickier:
    if (address.state.length) {
      // if there is a state, then assume there is also a city and street.
      // PROBLEM: city may be multiple words (spaces)
      // but we can pretty safely assume next-from-last token is at least PART of the city name
      // most cities are single-name. It would be very helpful if we knew more context, like
      // the name of the city user is in. But ignore that for now.
      // ideally would have Zip code service or lookup to give city name for the Zip code.
      var cityNameParts = [lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken];

      // assumption / RULE: street and city must have comma delimiter
      // addresses that do not follow this rule will be wrong only if city has space
      // but don't care because Esri formats put comma before City
      var streetNameParts = [];

      // check remaining tokens from right-to-left for the first comma
      while (2 + 2 != 5) {
        lastToken = tokens.pop();
        if (!lastToken) break;
        else if (lastToken.endsWith(",")) {
          // found end of street address (may include building, etc. - don't care right now)
          // add token back to end, but remove trailing comma (it did its job)
          tokens.Push(lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken);
          streetNameParts = tokens;
          break;
        } else {
          cityNameParts.unshift(lastToken);
        }
      }
      address.city = cityNameParts.join(' ');
      address.street = streetNameParts.join(' ');
    } else {
      // if there is NO state, then assume there is NO city also, just street! (easy)
      // reasoning: city names are not very original (Portland, OR and Portland, ME) so if user wants city they need to store state also (but if you are only ever in Portlan, OR, you don't care about city/state)
      // put last token back in list, then rejoin on space
      tokens.Push(lastToken);
      address.street = tokens.join(' ');
    }
  }
  // when parsing right-to-left hard to know if street only vs street + city/state
  // hack fix for now is to shift stuff around.
  // assumption/requirement: will always have at least street part; you will never just get "city, state"  
  // could possibly Tweak this with options or more intelligent parsing&sniffing
  if (!address.city && address.state) {
    address.city = address.state;
    address.state = '';
  }
  if (!address.street) {
    address.street = address.city;
    address.city = '';
  }

  return address;
}

// get list of objects with discrete address properties
var addresses = rawlist
  .filter(function(o) {
    return o.length > 0
  })
  .map(ParseAddressEsri);
$("#output").text(JSON.stringify(addresses));
console.log(addresses);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea>
27488 Stanford Ave, Bowden, North Dakota
380 New York St, Redlands, CA 92373
13212 E SPRAGUE AVE, FAIR VALLEY, MD 99201
1005 N Gravenstein Highway, Sebastopol CA 95472
A. P. Croll &amp; Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947
11522 Shawnee Road, Greenwood, DE 19950
144 Kings Highway, S.W. Dover, DE 19901
Intergrated Const. Services 2 Penns Way Suite 405, New Castle, DE 19720
Humes Realty 33 Bridle Ridge Court, Lewes, DE 19958
Nichols Excavation 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
P.O. Box 778, Dover, DE 19903
714 S Oak ST
714 S Oak ST, RUM TOWN, VA, 99201
3142 E SPRAGUE AVE, WHISKEY VALLEY, WA 99281
27488 Stanford Ave, Bowden, North Dakota
380 New York St, Redlands, CA 92373
</textarea>
<div id="output">
</div>

1
nothingisnecessary

Une autre option pour les adresses basées aux États-Unis est YAddress (créée par l'entreprise pour laquelle je travaille).

De nombreuses réponses à cette question suggèrent des outils de géocodage comme solution. Il est important de ne pas confondre l'analyse des adresses et le géocodage; Ils ne sont pas les mêmes. Les géocodeurs peuvent également décomposer une adresse en composants, mais ils reposent généralement sur des ensembles d'adresses non standard. Cela signifie qu'une adresse analysée par géocodeur peut ne pas être la même que l'adresse officielle. Par exemple, ce que l'API de géocodage de Google appelle "6th Ave" à Manhattan, USPS appelle "Avenue of the Americas".

0
Michael Diomin

Je suis en retard à la fête, voici un script Excel VBA que j'ai écrit il y a des années pour l'Australie. Il peut être facilement modifié pour prendre en charge d'autres pays. J'ai créé un référentiel GitHub du code C # ici. Je l'ai hébergé sur mon site et vous pouvez le télécharger ici: http://jeremythompson.net/rocks/ParseAddress.xlsm

Stratégie

Pour tous les pays ayant un code postal numérique ou pouvant être associé à un RegEx, ma stratégie fonctionne très bien:

  1. Nous détectons d’abord le prénom et le nom de famille qui sont supposés être la ligne du haut. Il est facile de sauter le nom et de commencer par l’adresse en décochant la case à cocher (appelée "Le nom est la rangée du haut" comme indiqué ci-dessous).

  2. Ensuite, vous pouvez vous attendre à ce que l'adresse composée de la rue et du numéro précède la banlieue et que St, Pde, Ave, Avenue, Rd, Cres, la boucle, etc. forment un séparateur.

  3. Détecter la banlieue par rapport à l'État et même au pays peut tromper les analyseurs syntaxiques les plus sophistiqués car il peut y avoir des conflits. Pour résoudre ce problème, j'utilise une recherche de code postal basée sur le fait qu'après avoir enlevé les numéros de rue et d'appartement/unité, ainsi que la PoBox, Ph, Fax , Mobile, etc., seul le code postal nombre restera. Ceci est facile à associer à un regEx pour rechercher ensuite la ou les banlieues et le pays.

Votre service postal national vous fournira gratuitement une liste de codes postaux avec les banlieues et les États que vous pourrez enregistrer dans une feuille Excel, un tableau de base de données, un fichier texte/json/xml, etc.

  1. Enfin, comme certains codes postaux ont plusieurs banlieues, nous vérifions quelle banlieue apparaît dans l’adresse.

Exemple

enter image description here

Code VBA

CLAUSE DE NON-RESPONSABILITÉ, je sais que ce code n’est pas parfait, ni même bien écrit. Cependant, il est très facile de convertir en tout langage de programmation et de l’utiliser dans n’importe quel type d’application. La stratégie est la réponse en fonction de votre pays et de vos règles. :

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
0
Jeremy Thompson

Si vous souhaitez vous appuyer sur OSM, les données libpostal sont très puissantes et gèrent un grand nombre des mises en garde les plus courantes concernant les entrées d'adresse.

0
Vitor Magalhães

Dans l'un de nos projets, nous avons utilisé l'analyseur d'adresses suivant. Il analyse les adresses de la plupart des pays du monde avec une bonne précision. 

http://address-parser.net/

Il est disponible en tant que bibliothèque autonome ou en tant qu'API dynamique. 

0
Waqas Anwar