从字符串中解析可用的街道地址,城市,州,邮政编码[关闭]


131

问题:我从Access数据库中获得了一个地址字段,该地址字段已转换为Sql Server2005。此字段在一个字段中包含所有内容。我需要将地址的各个部分解析为归一化表中的相应字段。我需要对大约4,000条记录执行此操作,并且它必须是可重复的。

假设:

  1. 假设在美国的地址(目前)

  2. 假设输入字符串有时会包含一个收件人(被寻址的人)和/或第二个街道地址(即Suite B)

  3. 状态可以缩写

  4. 邮政编码可以是标准的5位数或zip + 4

  5. 在某些情况下会有错别字

更新:针对提出的问题,没有普遍遵循标准,我需要存储单个值,而不仅是地址编码,而且错误意味着错字(已在上文更正)

样本数据:

  • AP Croll&Son 2299 Lewes-Georgetown Hwy,乔治敦,德国19947

  • 11522 Shawnee Road,格林伍德DE 19950

  • 得克萨斯州多佛,国王路144号,19901

  • 综合建筑。服务2 Penns Way Suite 405 New Castle,德国19720

  • Humes Realty 33 Bridle Ridge Court,刘易斯,德国19958

  • Nichols发掘2742 Pulaski Hwy Newark,DE 19711

  • 19904士麦那,2284 Bryn Zion Road

  • VEI Dover Crossroads,LLC蛇形路1500号,巴尔的摩MD 21套房100

  • 1990年,杜邦北道580号,多佛市

  • PO Box 778 Dover,DE 19903


几个问题:1.任何定界符?2.字符串中的字段顺序是什么?3.如果发生数据错误(EG将地址推入SQL表中的单个字段中,将其他字段留空),您想要什么行为
Jay Mooney

好问题和非常有趣的答案。从zip反向工作似乎是一个常见的主题,但是如果您从客户那里获取原始数据,则zip可能不准确。我猜想大多数站点的地址例如在90210中会不成比例。
凯文·威廉姆斯

4
@Kevin:是的,因为您美国人喜欢通过要求“邮政编码”来拒绝我们加拿大人,而不接受我们的邮政编码,从而迫使我们输入一些胡言乱语绕过系统....很不幸,我唯一的邮政编码知道是90210 :-)编辑:没关系...您显然在卑诗省与我住了几公里。然后,您可能也做同样的事情:-P
mpen 2010年

2
有关扩展的概述,请参见此SO问题
马特

Answers:


118

我在这种解析上做了很多工作。由于存在错误,您将无法获得100%的准确度,但是您可以通过一些方法来获得大部分结果,然后进行可视BS测试。这是一般的解决方法。它不是代码,因为编写代码很学术,没有怪异,只有很多字符串处理。

(现在您已经发布了一些示例数据,我做了一些小的更改)

  1. 向后工作。从邮政编码附近开始,该邮政编码将以两种已知格式之一开始,即结尾处,即XXXXX或XXXXX-XXXX。如果未出现,则可以假定您位于下面的城市,州部分。
  2. 在zip之前的下一件事将是状态,它将以两个字母的格式或以单词的形式出现。您也知道会是什么-只有50个。此外,您可以将单词发音,以帮助弥补拼写错误。
  3. 在那之前是城市,可能与州在同一条线上。您可以使用邮政编码数据库根据邮政编码检查城市和州,或者至少将其用作BS检测器。
  4. 街道地址通常为一两行。第二行通常是套房号(如果有),但也可以是邮政信箱。
  5. 在第一行或第二行上检测名称几乎是不可能的,尽管如果该名称没有前缀数字(或者如果前缀有“ attn:”或“ attention to:”,则可能会提示您是名称还是地址行。

我希望这会有所帮助。


14
虽然确实有50个州,但USPS表示,美国邮政局范围内有59个两个字母的缩写,如果算上美国武装部队,则为65个。usps.com/send/official-abbreviations.htm
Mike Sherrill'猫召回'

17
“仅50”表示该数字很小。它可能是“只有65”,但这对于解决眼前的问题并不重要。
蒂姆·沙利文


92

我认为外包问题是最好的选择:将其发送给Google(或Yahoo)地理编码器。地理编码器不仅返回纬度/经度(此处不感兴趣),还返回地址的丰富解析,并填写您未发送的字段(包括ZIP + 4和County)。

例如,解析“ 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]
      }
    }
  ]
}

现在可以解析了!


4
由于这是一个批处理过程,因此我也建议您使用线程池进行地址解析,以便您可以一次提交多个地址(谷歌支持任何类型的批处理接口吗?)
大卫

这对第二行地址(问题中的品脱5)并没有真正的帮助
Christopher Mahan 2010年

71
对于商业和/或非公共用途,服务条款通常是一个限制因素。
杰伊(Jay)

这是一个很好的解决方案,但在某些极端情况下,Google / Yahoo不会返回结果,例如,新地址和数据库中只是缺少的地址。
Peter DeWeese

这将是一个很好的解决方案,如果“ Google”不将批量调用限制在其MAPS api上
赫克托2015年

25

最初的海报可能已经进行了很长时间,但是我在移植由Perl 使用的Perl Geo :: StreetAddress:US模块时遇到了麻烦 geocoder.us到C#,它甩在CodePlex上,并认为,人们对未来过这个问题可能绊脚石觉得有用:

美国地址解析器

在项目的主页上,我尝试谈论其(非常真实的)局限性。由于它没有有效街道地址的USPS数据库支持,因此解析可能会很模糊,既无法确认也不否认给定地址的有效性。它可以尝试从字符串中提取数据。

这是针对以下情况的:您需要在正确的字段中主要获取一组数据,或者想要提供数据输入的快捷方式(让用户将地址粘贴到文本框中,而不是在多个字段之间进行制表)。它并不用于验证地址的可传递性。

它不会尝试解析街道线以上的任何东西,但是可能有人会用正则表达式弄虚作假,以使一些东西合理地接近-我可能只是将其分解为门牌号。


17

SmartyStreets具有一项新功能,可以从任意输入字符串中提取地址。(注意:我不在SmartyStreets工作。)

它成功地从上述问题中给出的样本输入中提取了所有地址。(顺便说一下,这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, S.W. 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,"P.O. 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,

我是最初编写服务的开发人员。我们实现的算法与此处的任何具体答案略有不同,但是每个提取的地址均根据地址查找API进行了验证,因此您可以确定其是否有效。每个经过验证的结果都是有保证的,但是我们知道其他结果并不是完美的,因为正如该线程中已经非常清楚地表明的那样,地址是不可预测的,即使对于人类来说有时也是不可预测的。


2
Smartystreets非常擅长于自己的工作。很高兴听到这是他们支持的API。
frottter

16

我过去曾经做过。

手动进行操作(构建一个不错的gui来帮助用户快速完成操作),或者使其自动化并根据最近的地址数据库进行检查(您必须购买)并手动处理错误。

每个人工处理大约需要10秒钟,这意味着您每小时可以执行3600/10 = 360,因此4000处理大约需要11-12个小时。这将为您提供较高的准确性。

为了实现自动化,您需要一个最新的美国地址数据库,并据此调整规则。我建议不要看待正则表达式(很难保持长期,有很多例外)。与数据库进行90%匹配,其余部分手动完成。

请在http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf上获取邮政地址标准(USPS)的副本,并注意它的长度超过130页。要实现的正则表达式会很麻烦。

对于国际地址,所有赌注都不可用。美国的工人将无法验证。

或者,使用数据服务。但是,我没有任何建议。

此外:当您确实发送邮件中的内容时(这是对的,对吗?),请确保将“请求的地址更正”放在信封上(在正确的位置)并更新数据库。(我们为前台人员创建了一个简单的gui来执行此操作;该人员实际上是对邮件进行排序的人)

最后,清理数据后,查找重复项。


14

根据这里的建议,我在VB中设计了以下功能,该功能创建可通过的数据,尽管并不总是完美的(如果给出了公司名称和套房名称,它将套房和城市结合在一起)。请随意评论/重构/大喊大叫,因为我违反了自己的规则之一,等等:

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,乔治敦,德国19947”返回:

2299 Lewes-Georgetown Hwy
A. P. Croll & Son  
Georgetown
DE
19947

13

我已经在地址处理领域工作了大约5年,但实际上并没有灵丹妙药。正确的解决方案将取决于数据的值。如果它不是很有价值,请按照其他答案的建议将其放入解析器。如果它甚至有价值,那么您肯定需要人工评估/纠正解析器的所有结果。如果您正在寻找一个完全自动化,可重复的解决方案,则可能需要与Group1或Trillium之类的地址更正供应商联系。


8

这不会解决您的问题,但是如果您仅需要这些地址的经/纬度数据,则Google Maps API会很好地解析未格式化的地址。

好的建议,或者您可以对Google地图的每个地址执行一个CURL请求,它将返回格式正确的地址。由此,您可以正则表达式适应您的内心需求。



6

地址的记录方式是否有任何标准?例如:

  1. 是否总是用逗号或换行符将street1与street2从城市从zip与州分开?
  2. 是否总是明确指出地址类型(道路,街道,林荫大道等)?总是缩写?每个都有吗?
  3. 定义“错误”。

我的一般答案是一系列正则表达式,尽管其复杂程度取决于答案。而且,如果根本没有一致性,那么您可能只能通过Regex实现部分成功(即:过滤出邮政编码和状态),并且必须手动完成其余工作(或者至少非常仔细地完成其余工作)仔细检查以确保发现错误)。


6

另一个请求样本数据的请求。

如前所述,我将从zip向后进行工作。

有了zip后,我将查询一个zip数据库,存储结果,然后从字符串中删除它们和zip。

这样会使您地址混乱。MOST(全部?)地址将以数字开头,因此在剩余的字符串中找到数字的第一个匹配项,然后抓取从该数字到字符串(新)末尾的所有内容。那就是你的地址。该号码左侧的任何内容都可能是收件人。

现在,您应该将城市,州和邮政编码存储在一个表中,并可能有两个字符串(收件人和地址)。对于地址,检查是否存在“套房”或“公寓”。等等,然后将其分为两个值(地址行1和2)。

对于收件人,我会平移并抓住该字符串的最后一个单词作为姓氏,然后将其余的放到名字字段中。如果您不想这样做,则需要在开始时检查称呼(先生,女士,博士等),并根据空格的数量对名称进行一些假设捏造。

我认为您无法以100%的准确度进行解析。


6

尝试www.address-parser.com。我们使用他们的网络服务,您可以在线进行测试


1
对于诸如在大型html文档中查找地址之类的东西,此方法效果很好。我只是希望他们有一个REST接口而不是SOAP。分享此链接的Thx。
jspooner 2011年

1
如果您与他们有联系,则必须披露这些信息。
马特

1
如果他们给出价格估算,而不是要求我在报价之前告诉他们服务的价值,那将是很好的。
烤面包机

5

根据样本数据:

  1. 我将从字符串的末尾开始。解析邮政编码(两种格式)。读到第一个空格。如果没有找到邮政编码错误。

  2. 修剪结尾,然后输入空格和特殊字符(逗号)

  3. 然后转到State,再次使用Space作为分隔符。也许使用查找列表来验证2个字母状态代码和完整的状态名称。如果找不到有效状态,则错误。

  4. 从末尾再次修剪空格和逗号。

  5. 城市变得棘手,实际上我会在此处使用逗号,以免城市中有太多数据。查找逗号或行首。

  6. 如果字符串中还剩下字符,请将所有字符都推到地址字段中。

这不是完美的,但是应该是一个很好的起点。


4

如果是人工输入的数据,那么您将花费太多时间尝试围绕异常进行编码。

尝试:

  1. 正则表达式以提取邮政编码

  2. 邮政编码查找(通过适当的政府数据库)以获取正确的地址

  3. 获取实习生以手动验证新数据是否与旧数据匹配


3

这不会解决您的问题,但是如果您仅需要这些地址的经/纬度数据,则Google Maps API会很好地解析未格式化的地址。


3

RecogniContact是解析美国和欧洲地址的Windows COM对象。您可以在http://www.loquisoft.com/index.php?page=8上尝试一下


RecogniContact似乎不对公众开放。访问需要提交联系表,但没有回复。也许其他人知道如何与他们取得联系。
卢克·范


3

由于数据中存在潜在的歧义,因此此类问题很难解决。

这是一个基于Perl的解决方案,该解决方案基于正则表达式定义了递归后代文法树,以解析街道地址的许多有效组合:http : //search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua /EN/AddressParse.pm。这包括地址内的子属性,例如:12 1st Avenue N Suite#2 Somewhere 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 Lewes-Georgetown Hwy,21994”。通过删除直到字符串中第一个数字的所有数据,可以轻松实现此目的。

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'

2

由于可能会出现单词错误,请考虑将SOUNDEX与LCS算法结合使用来比较字符串,这将大有帮助!


2

使用谷歌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);

1
这可能与ToS背道而驰,但看起来应该可以工作-尽管重新阅读了问题,但它并不完全符合要求。
杰米·布尔

2

对于红宝石或Rails开发人员,有一个不错的宝石,称为street_address。我一直在我的一个项目中使用它,它可以完成我需要的工作。

我唯一遇到的问题是,只要地址采用这种格式,P. O. Box 1410 Durham, NC 27702 它都会返回nil,因此我必须用''替换“ PO Box”,然后才能解析它。


指向上述模块的链接已损坏,请改用此代码:search.cpan.org/~kimryan/Lingua-EN-AddressParse
Kim Ryan

1

有些数据服务提供的邮政编码将为您提供该邮政编码中的街道名称列表。

使用正则表达式提取Zip或城市州-找到正确的州或城市,如果两者都出错。从数据源中提取街道列表校正城市和州,然后校正街道地址。获得有效的地址行1,城市,州和邮政编码后,您就可以在地址行2..3进行假设了。


1

我不知道这将是多么可行,但是我没有看到它被提及,所以我想我会建议这一点:

如果您严格在美国,...将获得一个庞大的数据库,其中包含所有邮政编码,州,城市和街道。现在在您的地址中查找这些地址。您可以通过测试(例如)所找到的城市是否在您所找到的州中,或者通过检查所找到的街道是否在所找到的城市中来验证所找到的内容。如果不是的话,约翰很有可能不在约翰的大街上,而是收件人的名字……基本上,获取尽可能多的信息并对照它检查地址。一个极端的例子是获取A中所有地址的清单,然后找出与您的每个地址最相关的地址...


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.