从字符串解析可用的街道地址,城市,州,邮编

时间:2008-08-19 15:34:42

标签: string parsing sql-server-2005 street-address

问题:我有一个来自Access数据库的地址字段,该数据库已转换为Sql Server 2005.此字段包含一个字段中的所有内容。我需要将地址的各个部分解析为规范化表中的相应字段。我需要为大约4,000条记录执行此操作,并且需要重复。

假设:

  1. 在美国(现在)假设地址

  2. 假设输入字符串有时包含收件人(被发送者)和/或第二个街道地址(即B组)

  3. 州可以缩写

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

  5. 在某些情况下存在拼写错误

  6. 更新:在回答提出的问题时,标准并未普遍遵循,我需要存储个别值,而不仅仅是地理编码和错误意味着拼写错误(在上面更正)

    示例数据:

    • 一个。 P. Croll& Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947

    • 11522 Shawnee Road,Greenwood DE 19950

    • 144 Kings Highway,S.W。多佛,DE 19901

    • 综合Const。服务2 Penns Way Suite 405 New Castle,DE 19720

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

    • Nichols挖掘2742 Pulaski Hwy Newark,DE 19711

    • 2284 Bryn Zion Road,Smyrna,DE 19904

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

    • 580 North Dupont Highway Dover,DE 19901

    • P.O。 Box 778 Dover,DE 19903

24 个答案:

答案 0 :(得分:117)

我在这种解析上做了很多工作。因为有错误你不会100%准确,但你可以做一些事情来完成大部分的工作,然后进行视觉BS测试。这是解决问题的一般方法。这不是代码,因为写它是相当学术的,没有任何古怪,只需要大量的字符串处理。

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

  1. 向后工作。从邮政编码开始,该邮政编码即将结束,并采用以下两种已知格式之一:XXXXX或XXXXX-XXXX。如果没有出现这种情况,您可以假设您位于城市,州下部分。
  2. 接下来,在拉链之前,将成为状态,它将是两个字母的格式,或者是单词。你知道它们会是什么 - 它们只有50个。此外,您可以对单词进行索引以帮助补偿拼写错误。
  3. 在此之前是城市,并且可能与州相同。您可以使用zip-code database根据zip检查城市和州,或者至少将其用作BS检测器。
  4. 街道地址通常为一两行。第二行通常是套件编号(如果有),但它也可以是邮政信箱。
  5. 在第一行或第二行检测名称几乎是不可能的,但如果它没有前缀数字(或者如果它的前缀是“attn:”或“注意:”它可能会给你关于它是名称还是地址线的提示。
  6. 我希望这会有所帮助。

答案 1 :(得分:93)

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

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

现在那是可解析的!

答案 2 :(得分:25)

原始海报可能已经很久了,但是我试图将Geo::StreetAddress:US使用的Perl geocoder.us模块移植到C#,将其转储到CodePlex上,并认为人们在这个问题上遇到了绊脚石在将来可能会发现它很有用:

US Address Parser

在项目的主页上,我试着谈谈它(非常真实)的限制。由于没有有效街道地址的USPS数据库支持,解析可能是模糊的,它不能确认或否认给定地址的有效性。它可以尝试从字符串中提取数据。

这意味着当您需要在正确的字段中获取一组数据,或者想要提供数据输入的快捷方式时(允许用户将地址粘贴到文本框中而不是在多个文本框之间进行标记)字段)。 用于验证地址的可传递性。

它并没有试图解析街道上方的任何东西,但是人们可能会对正则表达式进行合理的处理以获得相当接近的东西 - 我可能只是在门牌号码处将其分解。 / p>

答案 3 :(得分:16)

我过去做过这个。

要么手动完成,(构建一个帮助用户快速完成的优秀gui),要么让它自动化并检查最近的地址数据库(你必须购买)并手动处理错误。

手动处理每个大约需要10秒钟,这意味着你可以每小时做3600/10 = 360,所以4000大约需要11-12个小时。这将为您提供高准确率。

对于自动化,您需要最近的美国地址数据库,并根据该数据库调整您的规则。我建议不要对正则表达式感兴趣(难以维持长期,很多例外)。与数据库进行90%匹配,手动完成其余操作。

请在http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf获取邮政地址标准(USPS)的副本,并注意它长达130多页。实施的正则规则将是坚果。

对于国际地址,所有投注都已关闭。美国的工人无法验证。

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

此外:当您确实发送邮件中的内容时(这就是它的用途,对吗?)请确保在信封上(在正确的位置)放置“请求地址更正”并更新数据库。 (我们为前台人员制作了一个简单的gui;实际通过邮件进行分类的人)

最后,当您清理数据时,请查找重复项。

答案 4 :(得分: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功能“A. P. Croll&amp; Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947”返回:

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

答案 5 :(得分:13)

我已经在地址处理领域工作了大约5年了,而且真的没有银弹。正确的解决方案将取决于数据的价值。如果它不是很有价值,请将其通过解析器抛出,如其他答案所示。如果它甚至有点贵重,你肯定需要让人来评估/纠正解析器的所有结果。如果您正在寻找一个完全自动化,可重复的解决方案,您可能希望与Group1或Trillium等地址更正供应商交谈。

答案 6 :(得分:9)

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

它成功地从上面问题中给出的样本输入中提取了所有地址。 (顺便说一下,这10个地址中只有9个是有效的。)

以下是一些输出:enter image description here

这是同一请求的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进行验证的,因此您可以确定它是否有效。每个经过验证的结果都是有保证的,但是我们知道其他结果并不完美,因为在这个帖子中已经非常明确地 ,地址是不可预测的,即使对于人类来说也是如此。

答案 7 :(得分:8)

  

这不会解决您的问题,但如果   你只需要lat / long数据   这些地址是Google Maps API   将解析非格式化的地址   很好。

好的建议,或者您可以为Google地图执行每个地址的CURL请求,它将返回格式正确的地址。从那以后,你就可以正念你的内容了。

答案 8 :(得分:7)

关于詹姆斯·罗森建议的解决方案+1,因为它对我来说效果很好,但是对于小组成员而言,这个网站是一本引人入胜的读物,也是我在记录全球地址时所看到的最好的尝试:http://www.columbia.edu/kermit/postal.html

答案 9 :(得分:6)

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

  1. 是否总是用逗号或换行符将street1与street2从城市从州与zip分开?
  2. 地址类型(道路,街道,林荫大道等)是否总是拼写出来?总是缩写?一些?
  3. 定义“错误”。
  4. 我的一般答案是一系列正则表达式,但这种复杂性取决于答案。如果根本没有一致性,那么你可能只能通过正则表达式获得部分成功(即:过滤掉邮政编码和状态)并且必须手工完成剩下的工作(或者至少要完成剩下的工作)仔细确保你发现错误。)

答案 10 :(得分:6)

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

答案 11 :(得分:6)

另一个样本数据请求。

如前所述,我会从拉链后退。

一旦你有了一个zip我会查询一个zip数据库,存储结果,然后删除它们&amp;从字符串拉链。

这会让你的地址变得混乱。 MOST(全部?)地址将以数字开头,因此在剩余的字符串中找到第一个出现的数字,并从中捕获所有地址到字符串的(新)结尾。那将是你的地址。该号码左侧的任何内容都可能是收件人。

你现在应该拥有City,State,&amp; Zip存储在一个表中,可能还有两个字符串,收件人和地址。对于地址,请检查是否存在“套房”或“公寓”。然后将其分成两个值(地址行1和2)。

对于收件人,我会将该字符串的最后一个字作为姓​​氏,并将其余部分放入名字字段。如果你不想这样做,你需要在开始时检查称呼(先生,女士,博士等),并根据名称的空格数做出一些假设。捏造。

我认为没有任何方法可以100%准确地解析。

答案 12 :(得分:5)

基于样本数据:

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

  2. 然后修剪空格和特殊字符(逗号)

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

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

  5. 城市变得棘手,我实际上会在这里使用逗号,冒着在城市中获取太多数据的风险。查找逗号或行的开头。

  6. 如果您仍然在字符串中留下字符,请将所有字符推入地址字段。

  7. 这并不完美,但它应该是一个非常好的起点。

答案 13 :(得分:4)

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

尝试:

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

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

  3. 让实习生手动验证新数据是否符合旧

答案 14 :(得分: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,但也适用于非美国的地址,例如英国,澳大利亚和加拿大。

以下是您的一个示例地址的输出。注意,名称部分需要首先从“A. P. Croll&amp; Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947”中删除,以将其减少为“2299 Lewes-Georgetown Hwy,Georgetown,DE 19947”。这可以通过删除字符串中找到的第一个数字来轻松实现。

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'

答案 15 :(得分:3)

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

答案 16 :(得分:3)

你可能想看看这个!! http://jgeocoder.sourceforge.net/parser.html 对我来说就像一个魅力。

答案 17 :(得分:3)

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

答案 18 :(得分:2)

由于单词中存在错误的可能性,考虑使用SOUNDEX结合LCS算法来比较字符串,这将有很大帮助!

答案 19 :(得分: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);

答案 20 :(得分:2)

对于ruby或rails开发人员,有一个名为street_address的好宝石。 我一直在我的一个项目中使用它,它完成了我需要的工作。

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

答案 21 :(得分:1)

我不知道这会有多少可行,但我没有看到这提到,所以我想我会继续建议:

如果您严格在美国......请获取所有邮政编码,州,城市和街道的庞大数据库。现在在你的地址中查找这些内容。您可以通过测试,例如,您找到的所在城市,或者通过检查您找到的街道是否存在于您找到的城市中来验证您找到的内容。如果没有,John很可能不是约翰的街道,而是收件人的名字......基本上,你可以得到最多的信息并检查你的地址。 一个极端的例子是获得A的所有地址列表,然后找出哪个地址与您的每个地址最相关......

答案 22 :(得分:1)

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

使用正则表达式提取Zip或城市状态 - 找到正确的一个或如果错误同时获取。 从data source拉出街道列表更正城市和州,然后街道地址。一旦获得有效的地址行1,城市,州和邮政编码,您就可以在地址行2 ... 3上做出假设。

答案 23 :(得分:1)

有perl Geo :: StreetAddress :: US包的javascript端口:https://github.com/hassansin/parse-address。它是基于正则表达式并且工作得很好。