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

Rob*_*len 128 string parsing sql-server-2005 street-address

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

假设:

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

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

  3. 国家可以缩写

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

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

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

样本数据:

  • AP Croll&Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947

  • 11522 Shawnee Road,Greenwood DE 19950

  • 144 Kings Highway,SW Dover,DE 19901

  • 综合Const.服务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

  • PO Box 778 Dover,DE 19903

Tim*_*van 117

我在这种解析方面做了很多工作.因为有错误,你不会得到100%的准确率,但也有一些事情你可以做的就是最那里的方式,然后做一次视力测试学士学位.这是解决问题的一般方法.这不是代码,因为它是相当的学术把它写,没有的怪事,只是大量的字符串处理.

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

  1. 向后工作.从邮政编码开始,该邮政编码即将结束,并采用以下两种已知格式之一:XXXXX或XXXXX-XXXX.如果没有出现,你可以假设你在城市,州的一部分,下面.
  2. 拉链之前的下一件事就是状态,它将采用双字母格式或单词形式.你知道它们会是什么 - 它们只有50个.此外,您可以对单词进行索引以帮助补偿拼写错误.
  3. 在那之前是城市,它可能与国家在同一条线上.您可以使用邮政编码数据库根据zip检查城市和州,或者至少将其用作BS检测器.
  4. 街道地址通常是一两行.第二行通常是套件编号(如果有),但它也可以是PO框.
  5. 在第一行或第二行检测名称几乎是不可能的,但如果它没有前缀数字(或者它的前缀是"attn:"或"注意:"它可能会给你一个提示无论是名称还是地址线.

我希望这有点帮助.

  • "只有50"表示这个数字非常小.它可能只有"65",但这对于解决手头的问题并不重要. (15认同)
  • 虽然确实有50个州,USPS表示美国邮政服务领域内有59个双字母缩写,如果算上美国武装部队,则为65个.https://www.usps.com/send/official-abbreviations.htm (13认同)
  • 该算法也在[USPS Publication 28](http://pe.usps.gov/cpim/ftp/pubs/pub28/pub28.pdf)中详述. (4认同)

Jam*_*sen 93

我认为外包问题是最好的选择:将其发送给谷歌(或雅虎)地理编码器.地理编码器不仅返回lat/long(这里不感兴趣),而且还返回地址的丰富解析,填写了未发送的字段(包括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]
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

现在这是可以解决的!

  • 对于商业和/或非公共用途,服务条款通常是限制因素. (70认同)
  • 由于这是一个批处理过程,我还建议使用线程池进行地理编码,以便您可以一次提交多个地址(Google是否支持任何类型的批处理界面?) (4认同)

Nic*_*cki 25

原来的海报可能长期感动,但我花了刺在移植Perl的地理::的StreetAddress:美国模块所使用的geocoder.us到C#,它甩在CodePlex上,并认为,人们对未来过这个问题可能绊脚石发现它很有用:

美国地址解析器

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

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

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


Chr*_*han 16

我过去做过这个.

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

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

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

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

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

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

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

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


Rob*_*len 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
Run Code Online (Sandbox Code Playgroud)

通过parseAddress功能"AP Croll&Son 2299 Lewes-Georgetown Hwy,Georgetown,DE 19947"返回:

2299 Lewes-Georgetown Hwy
A. P. Croll & Son  
Georgetown
DE
19947
Run Code Online (Sandbox Code Playgroud)


Nic*_*dem 13

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


Mat*_*att 9

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,
Run Code Online (Sandbox Code Playgroud)

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

  • Smartystreets 非常擅长他们的工作。很高兴听到这是他们支持的 API。 (2认同)

Kev*_*vin 8

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

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


wes*_*ton 7

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


Yaa*_*lis 6

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

  1. 是否总是用逗号或新线将street1从street2与city从state分隔开来?
  2. 地址类型(道路,街道,林荫大道等)是否总是拼写出来?总是缩写?一些?
  3. 定义"错误".

我的一般答案是一系列正则表达式,但这种复杂性取决于答案.如果根本没有一致性,那么你可能只能通过正则表达式获得部分成功(即:过滤掉邮政编码和状态)并且必须手工完成剩下的工作(或者至少要完成剩下的工作)仔细确保你发现错误).


Chu*_*uck 6

另一个样本数据请求.

如前所述,我会从拉链向后工作.

一旦你有一个zip我会查询一个zip数据库,存储结果,并从字符串中删除它们和zip.

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

您现在应该将City,State和Zip存储在一个表中,可能还有两个字符串,收件人和地址.对于地址,请检查是否存在"套房"或"公寓".等,并将其分成两个值(地址行1和2).

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

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


小智 6

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


Jay*_*ney 5

根据样本数据:

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

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

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

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

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

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

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