parsing可用的街道地址,城市,州,从一个string拉链
问题:我有一个来自Access数据库的地址字段,该数据库已被转换为Sql Server 2005.该字段包含所有内容。 我需要将地址的各个部分parsing到标准化表格中相应的字段中。 我需要为约4000条logging做这个,它需要是可重复的。
假设:
-
假设在美国的地址(现在)
-
假定inputstring有时包含收信人(被寻址的人)和/或第二个街道地址(即套房B)
-
州可能会缩写
-
邮政编码可以是标准的5位或邮编+4
-
在某些情况下有拼写错误
更新:在回答提出的问题,标准没有普遍遵循,我需要存储的个人价值观,不只是地理编码和错误意味着错字(上述纠正)
示例数据:
-
AP Croll&Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947
-
Shawnee路11522,格林伍德DE 19950
-
144国王高速公路,SW多弗,DE 19901
-
集成的常量 服务2 Penns Way套房405 New Castle,DE 19720
-
Humes Realty 33 Bridle Ridge Court,Lewes,DE 19958
-
Nichols挖掘2742 Pulaski Hwy纽瓦克,DE 19711
-
2284布赖恩锡安路,士麦那,DE 19904
-
VEI多佛十字路口有限责任公司1500 Serpentine Road,Suite 100 Baltimore MD 21
-
580北杜邦高速公路多佛,DE 19901
-
PO Box 778 Dover,DE 19903
我在这种parsing方面做了很多工作。 因为存在错误,所以你不能100%的准确,但是有几件事情可以做到最好,然后做一个可视化的BStesting。 以下是一般的方法。 这不是代码,因为写它是非常的学术,没有古怪,只是很多string处理。
(现在你已经发布了一些示例数据,我做了一些小改动)
- 倒退。 从接近结尾的邮政编码开始,使用两种已知格式之一:XXXXX或XXXXX-XXXX。 如果没有出现,你可以假设你在城市,州部分,下面。
- 接下来的事情,在拉链之前,将成为国家,这将是两个字母的格式,或作为文字。 你也知道这些会是什么 – 只有50个。 另外,你可以将这些单词拼写出来,以帮助弥补拼写错误。
- 在那之前就是这个城市,而且这个城市可能和国家在同一条线上。 你可以使用一个邮政编码数据库来检查城市和州的基础上的压缩,或至less使用它作为一个BS检测器。
- 街道地址通常是一两行。 第二行一般是套房号,如果有的话,也可以是邮政信箱。
- 在第一行或第二行检测一个名字几乎是不可能的,但是如果它没有以数字作为前缀的话(或者如果它的前缀是“attn:”或者“attention to:”,它可以给你提示不pipe是名字还是地址。
我希望这有所帮助。
我认为把这个问题外包是最好的select:将它发送给Google(或Yahoo)的地理编码器。 地理编码器不仅返回纬度/经度(这里不感兴趣),而且还有丰富的地址parsing,填充的字段没有发送(包括ZIP + 4和县)。
例如,parsing“1600 Amphitheatre Parkway,Mountain View,CA”
{ "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] } } ] }
现在可以parsing!
原来的海报可能已经很久了,但是我试图将用于geocoder.us的Perl Geo :: StreetAddress:美国模块移植到C#中,将它转移到CodePlex上,并且认为将来人们可能会绊倒这个问题觉得有用:
美国地址parsing器
在项目的主页上,我试图谈论它的(非常真实的)限制。 由于它没有得到有效街道地址的USPS数据库的支持,parsing可能是模棱两可的,它不能确认也不否认给定地址的有效性。 它可以试图从string中提取数据。
这意味着您需要主要在正确的字段中获取一组数据,或者希望提供数据input的快捷方式(允许用户将地址粘贴到文本框中,而不是在多个字段之间进行切换)。 这并不意味着要validation地址的可交付性。
它不会试图parsing出街道以上的任何东西,但是可能会用正则expression式来弥补一些相当接近的东西 – 我可能只是在门牌号上打破它。
我过去做过这个
要么手动(build立一个很好的GUI,帮助用户快速完成)或自动化,并检查最近的地址数据库(你必须购买)和手动处理错误。
手动处理大约需要10秒,这意味着每小时可以做3600/10 = 360,所以4000应该花费大约11-12小时。 这会给你一个很高的准确率。
对于自动化,你需要一个最近的美国地址数据库,并调整你的规则。 我build议不要看正则expression式(很难保持长期,很多例外)。 对数据库进行90%的匹配,手动完成剩下的工作。
请在http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf上获得邮政寻址标准(USPS)的副本,并注意它有130多页长。; 正则expression式实现这将是坚果。
对于国际地址,所有投注都closures。 美国的工人将无法validation。
或者,使用数据服务。 但是,我没有任何build议。
此外:当你发送邮件中的东西(这是什么意思,对吗?)确保你在信封上(正确的地方)提出“请求地址更正”并更新数据库。 (我们为前台的人做了一个简单的gui,实际上通过邮件分类的人)
最后,当您清理数据时,请查找重复项。
我已经在地址处理领域工作了5年左右,真的没有银弹。 正确的解决scheme将取决于数据的价值。 如果它不是很有价值的话,就像其他答案所build议的那样通过parsing器。 如果它甚至有点贵重,那么肯定需要有人工评估/校正parsing器的所有结果。 如果你正在寻找一个完全自动化的,可重复的解决scheme,你可能想要与一个地址更正厂商如Group1或Trillium交谈。
在这里的build议之后,我在VB中devise了以下function,虽然并不总是完美的(如果给出了一个公司名称和一个套件行,它结合了套件和城市)可用的数据,在VB中创build了可通过的以下function。 请随意评论/重构/抨击我违反我自己的规则之一:
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
通过parseAddress
函数“AP Croll&Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947”返回:
2299 Lewes-Georgetown Hwy AP Croll & Son Georgetown DE 19947
SmartyStreets具有从任意inputstring中提取地址的新function。 (注意:我不在SmartyStreets工作。)
它成功提取了上面问题中给出的input样本中的所有地址。 (顺便说一句,这10个地址中只有9个是有效的。)
以下是一些输出:
以下是同一个请求的CSV格式的输出:
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,
我是最初编写这个服务的开发者。 我们实现的algorithm与这里的任何具体答案有些不同,但是每个提取的地址都是通过地址查找API进行validation的,所以您可以确定它是否有效。 每个validation的结果都是有保证的,但是我们知道其他的结果并不完美,因为在这个线程中已经非常清楚 ,地址是不可预知的,即使对于人类来说也是如此。
这不会解决您的问题,但如果您只需要这些地址的纬度/经度数据,Google Maps API将非常好地parsing非格式化的地址。
好的build议,或者你可以执行每个地址的谷歌地图的CURL请求,它会返回格式正确的地址。 从这个angular度来看,你可以正确地理解你的内心。
詹姆斯·Rosenbuild议的解决scheme+1,因为它对我来说工作得很好,但是对于完成者来说,这个站点是一个迷人的阅读和我在全球范围内logging地址的最佳尝试: http : //www.columbia.edu/kermit /postal.html
有没有任何标准的地址logging方式? 例如:
- 是否总是有逗号或新的行,将街道1与街道2从城市分离出来,并从拉链中分离出来?
- 地址types(道路,街道,林荫大道等)是否总是拼写出来? 总是缩写? 每一个?
- 定义“错误”。
我的一般答案是一系列正则expression式,虽然这个复杂性取决于答案。 如果没有一致性,那么你可能只能通过正则expression式(即:过滤出邮政编码和状态)才能取得部分成功,并且必须手工完成剩下的工作(或者至less经过其余的工作仔细确保你发现错误)。
样品数据的另一个要求。
正如已经提到的,我会从邮编工作。
一旦你有一个zip,我会查询一个zip数据库,存储结果,并从string中删除它们和zip。
这会让你的地址混乱。 MOST(All?)地址将以一个数字开头,以便在其余string中find第一个数字,并从string的(新)尾部抓取所有数字。 那将是你的地址。 该号码左边的任何内容都可能是收件人。
你现在应该把City,State和Zip存储在一个表中,并且可能有两个string,收件人和地址。 对于地址,检查是否存在“Suite”或“Apt”。 并将其分成两个值(地址线1和2)。
对于收件人,我会踢,并抓住该string的最后一个字作为姓,并把其余的名字字段。 如果你不想这样做,你需要在开始时检查(先生,女士,博士等)的称呼,并根据空格的数量做出一些假设,捏造。
我不认为有任何方法可以100%的精确度进行分析。
尝试www.address-parser.com 。 我们使用他们的networking服务,你可以在线testing
根据样本数据:
-
我会开始在string的末尾。 parsing一个Zip代码(两种格式)。 阅读结束的第一个空间。 如果没有find邮政编码错误。
-
修剪结束然后空格和特殊字符(逗号)
-
然后转到状态,再次使用空格作为分隔符。 也许使用查找列表来validation2个字母的状态代码和完整的状态名称。 如果找不到有效的状态,则报错。
-
再次修剪空格和逗号。
-
城市变得棘手,我实际上在这里使用逗号,冒着在城市中获取太多数据的风险。 寻找逗号,或开始行。
-
如果string中仍有字符,则将其全部移到地址字段中。
这不是完美的,但它应该是一个很好的起点。
如果是人类input的数据,那么您将花费太多时间来尝试对exception进行编码。
尝试:
-
正则expression式来提取邮政编码
-
邮政编码查询(通过适当的政府数据库)以获得正确的地址
-
让实习生手动validation新的数据是否与旧的数据匹配
这不会解决您的问题,但如果您只需要这些地址的纬度/经度数据,Google Maps API将非常好地parsing非格式化的地址。
RecogniContact是分析美国和欧洲地址的Windows COM对象。 你可以试试http://www.loquisoft.com/index.php?page=8
这种types的问题很难解决,因为数据中含糊不清。
这是一个基于Perl的解决scheme,它定义了一个基于正则expression式的recursion下降语法树来parsing许多有效的街道地址组合: http : //search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua /EN/AddressParse.pm 。 这包括地址内的子属性,例如:12第一大道N套房#2某处CA 12345 USA
它类似于上面提到的http://search.cpan.org/~timb/Geo-StreetAddress-US-1.03/US.pm ,但也适用于不是来自美国的地址,例如英国,澳大利亚和加拿大。
以下是您的一个示例地址的输出。 请注意,名称部分需要首先从“AP Croll&Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947”中删除,以将其缩减为“DE 19947 Georgetown,Lewes-Georgetown Hwy 2299”。 删除string中第一个数字的所有数据很容易实现。
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'
由于单词中有错误的机会,可以考虑使用SOUNDEX和LCSalgorithm结合来比较string,这将有很大的帮助!
使用谷歌API
$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);
对于Ruby或Rails开发者来说,有一个很好的gem可以叫做street_address 。 我一直在使用这个在我的项目之一,它做我需要的工作。
我唯一的问题是每当一个地址是这种格式的PO Box 1410 Durham, NC 27702
它返回零,因此我不得不取代“邮政信箱”的“”,在此之后,它能够parsing它。
有数据服务,给定一个邮政编码会给你在该邮政编码的街道名称列表。
使用正则expression式来提取邮政编码或城市状态 – find正确的或错误都得到。 从数据源中拉出街道列表更正城市和州,然后街道地址。 一旦得到有效的地址栏1,城市,州和邮政编码,您就可以在地址栏2..3上作出假设
我不知道这可能是多么可能,但我没有看到这个提到,所以我想我会继续前进,并build议这样做:
如果你严格在美国…得到一个庞大的所有邮政编码,州,城市和街道的数据库。 现在在你的地址寻找这些。 例如,如果您find的城市存在于您find的州中,或者通过查看您find的街道是否存在于您find的城市中,则可以validation您所发现的内容。 如果不是,约翰不是约翰的街道,而是收件人的名字…基本上,获取最多的信息,并检查你的地址。 一个极端的例子是获得所有在美国的地址列表,然后find哪一个与您的每个地址最相关的匹配…
有perl Geo :: StreetAddress :: US包的JavaScript端口: https : //github.com/hassansin/parse-address 。 这是基于正则expression式,工作得很好。