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:
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?
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.
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.
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:
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).
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.
Le numéro 3 est un format de boîte postale complet, car il contient un code postal.
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.
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
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.
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!
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é.
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:
http://www.gisgraphy.com Gratuit, avec des services Web de géocodeur et de géolocalisation prêts à utiliser, intégrant OpenStreetMap, GeoNames et Quattroshapes.
https://github.com/kodapan/osm-common Bibliothèque d’accès aux services OpenStreetMap, d’analyse et de traitement des données.
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.
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
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
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"}
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 & 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>
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".
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
Pour tous les pays ayant un code postal numérique ou pouvant être associé à un RegEx, ma stratégie fonctionne très bien:
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).
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.
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.
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
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.
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.
Il est disponible en tant que bibliothèque autonome ou en tant qu'API dynamique.