代码之家  ›  专栏  ›  技术社区  ›  Nick

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

  •  125
  • Nick  · 技术社区  · 16 年前

    问题:我有一个Access数据库中的地址字段,该数据库已转换为SQL Server 2005。这个字段在一个字段中包含所有内容。我需要将地址的各个部分解析成一个规范化表中相应的字段。我需要对大约4000个记录执行此操作,并且它需要可重复。

    假设:

    1. 假设在美国有一个地址(目前)

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

    3. 州可以缩写

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

    5. 有些情况下有错别字

    更新:对于提出的问题,标准没有得到普遍遵循,我需要存储个别的值,而不仅仅是地理编码和错误意味着打字错误(更正以上)

    样本数据:

    • A.P.Croll&Son 2299 Lewes Georgetown Hwy,Georgetown,de 19947

    • 格林伍德肖尼路11522号,邮编:19950

    • 144 Kings公路,S.W.Dover,DE 19901

    • 综合常数服务2 Penns Way Suite 405 New Castle,DE 19720

    • 休姆斯房地产33 Bridle Ridge Court,Lewes,de 19958

    • 尼科尔斯挖掘2742 Pulski Hwy Newark,de 19711

    • 地址:2284 Bryn Zion Road,Smyrna,de 19904

    • Vei Dover Crossroads,LLC 1500蛇形路,马里兰州巴尔的摩100号套房,21

    • 达佛北杜邦公路580号,邮编:19901

    • 邮政信箱778 Dover,DE 19903

    24 回复  |  直到 10 年前
        1
  •  114
  •   Tim Sullivan    16 年前

    我在这种解析上做了很多工作。因为有错误,你不会得到100%的准确度,但有一些事情你可以做,以获得大部分的方式,然后做一个视觉的BS测试。这是解决问题的一般方法。这不是代码,因为写代码很学术,没有什么奇怪之处,只是需要大量的字符串处理。

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

    1. 工作落后。从靠近末尾的邮政编码开始,并采用两种已知格式之一:XXXX或XXXX-XXXX。如果没有出现这种情况,您可以假设您在下面的城市、州部分。
    2. 下一件事,在邮政编码之前,将是州,它将以两个字母的格式,或者作为单词。你也知道这些是什么——只有50个。此外,您还可以用soundex来帮助补偿拼写错误。
    3. 在那之前是城市 可能 和州政府在同一条线上。你可以使用 zip-code database 根据邮政编码检查城市和州,或者至少将其用作BS探测器。
    4. 街道地址通常是一到两行。第二行通常是套房号(如果有),但也可以是邮政信箱。
    5. 在第一行或第二行上检测一个名字几乎是不可能的,但是如果它没有加前缀的数字(或者如果它加前缀的是“attn:”或“attention to:”,它可以提示你它是一个名字还是一个地址行。

    我希望这能有所帮助。

        2
  •  93
  •   James A. Rosen    16 年前

    我认为外包这个问题是最好的选择:把它发送到谷歌(或雅虎)的地理编码器。geocoder不仅返回lat/long(这里不感兴趣),还返回对地址的丰富解析,其中填充了未发送的字段(包括zip+4和county)。

    例如,解析“1600圆形剧场公园路,山景,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]
          }
        }
      ]
    }
    

    现在 那是 可解析的!

        3
  •  25
  •   Nicholas Piasecki    13 年前

    最初的海报可能已经持续很久了,但是我尝试了移植Perl Geo::StreetAddress:US 使用的模块 geocoder.us 对于C,把它扔到codeplex上,并认为在将来遇到这个问题的人可能会发现它是有用的:

    US Address Parser

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

    它适用于这样的情况:当您需要获取一组数据时,主要是在正确的字段中,或者希望提供数据输入的快捷方式(允许用户将地址粘贴到文本框中,而不是在多个字段之间切换)。它是 用于验证地址的可传递性。

    它并没有试图解析出街道线以上的任何东西,但有人可能会和regex混在一起,得到一些相当接近的东西——我可能只是在门牌号处把它分开。

        4
  •  16
  •   Christopher Mahan    16 年前

    我以前做过。

    要么手动操作,(构建一个好的图形用户界面,帮助用户快速操作),要么让它自动操作,对照最近的地址数据库进行检查(你必须购买它),然后手动处理错误。

    手动操作每个大约需要10秒,这意味着您可以每小时3600/10=360,所以4000应该需要大约11-12小时。这将给你一个高的准确率。

    为了自动化,您 需要 一个最新的美国地址数据库,并根据它调整您的规则。我建议不要喜欢regex(很难长期维护,很多例外)。对数据库进行90%匹配,然后手动执行其余操作。

    请务必在以下网址获取邮政寻址标准(USPS)的副本: http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf 注意它有130多页长。要实现的正则表达式将是螺母。

    对于国际地址,所有赌注都取消了。美国工人将无法验证。

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

    此外:当你把邮件里的东西发出去的时候(这就是它的目的,对吗?)确保您在信封上(在正确的位置)写上“请求地址更正”,并且 更新 数据库。(我们为前台人员做了一个简单的图形用户界面;实际整理邮件的人员)

    最后,当您清除了数据后,寻找重复项。

        5
  •  13
  •   Nicholas Trandem    16 年前

    我已经在地址处理领域工作了5年左右,现在真的没有什么好消息。正确的解决方案将取决于数据的值。如果它不是很有价值,那么像其他答案所建议的那样,将它通过解析器。如果它甚至有点价值,那么您肯定需要一个人工评估/纠正解析器的所有结果。如果您正在寻找一个完全自动化、可重复的解决方案,那么您可能需要与地址更正供应商(如Group1或Trillium)交谈。

        6
  •  13
  •   Tim Huntrods    11 年前

    在这里的建议之后,我在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&son 2299 Lewes Georgetown Hwy,Georgetown,de 19947”返回:

    2299 Lewes-Georgetown Hwy
    A. P. Croll & Son  
    Georgetown
    DE
    19947
    
        7
  •  9
  •   Matt    10 年前

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

    它成功地从上述问题中给出的示例输入中提取了所有地址。(顺便说一下,这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验证的,因此您可以确定它是否有效。每一个验证结果都是有保证的,但我们知道其他结果并不完美,因为 非常清楚 在这个线程中,地址是不可预测的,甚至有时对人类也是如此。

        8
  •  8
  •   Kevin    16 年前

    这不能解决你的问题,但是如果 您只需要lat/long数据 这些地址,谷歌地图API 将分析未格式化的地址 很好。

    很好的建议,或者你可以对谷歌地图的每个地址执行curl请求,它将返回正确格式的地址。从那以后,你就可以随心所欲了。

        9
  •  7
  •   weston    13 年前

    +1关于James A.Rosen的建议解决方案,因为它对我很有效,但是对于完美主义者来说,这个网站是一本引人入胜的书,也是我在记录全球地址方面所看到的最好的尝试: http://www.columbia.edu/kermit/postal.html

        10
  •  6
  •   Yaakov Ellis NevilleDNZ    16 年前

    记录地址的方式有什么标准吗?例如:

    1. 街道1和街道2、城市和州与邮政编码之间是否总是有通信或新线路分隔?
    2. 地址类型(道路、街道、林荫道等)是否总是拼写清楚?总是缩写?每一个?
    3. 定义“错误”。

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

        11
  •  6
  •   Brian M. Hunt    16 年前

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

    如前所述,我将从拉链向后工作。

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

    这会让你的地址一团糟。大多数(全部?)地址将以数字开头,因此在剩余的字符串中查找第一个出现的数字,并从该数字到字符串的(新)结尾获取所有内容。那是你的地址。那个号码左边的任何东西都可能是收件人。

    您现在应该将城市、州和邮政编码存储在一个表中,并且可能包含两个字符串:收件人和地址。对于地址,检查是否存在“suite”或“apt”等,并将其分为两个值(地址行1和2)。

    对于收信人,我会把字符串的最后一个单词作为姓氏,然后将其余的单词放在名字字段中。如果您不想这样做,您需要在开始时检查称谓(先生、女士、博士等),并根据空格数对名称的构成方式进行一些假设。

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

        12
  •  6
  •   We Know    15 年前

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

        13
  •  5
  •   Jay Mooney    16 年前

    根据样本数据:

    1. 我会从绳子的末端开始。分析邮政编码(任一格式)。从结尾到第一个空格。如果没有找到邮政编码错误。

    2. 修剪末端,然后用于空格和特殊字符(逗号)

    3. 然后转到状态,再次使用空格作为分隔符。可以使用查找列表来验证两个字母的状态代码和完整的状态名称。如果找不到有效状态,则返回错误。

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

    5. 城市变得很棘手,我会在这里使用逗号,以防在城市中获取太多数据。查找逗号或行首。

    6. 如果字符串中还留有字符,请将所有字符都放入地址字段。

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

        14
  •  4
  •   engtech    16 年前

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

    尝试:

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

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

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

        15
  •  3
  •   pix0r    16 年前

    这不会解决您的问题,但是如果您只需要这些地址的lat/long数据,那么google maps api将很好地解析非格式化地址。

        16
  •  3
  •   Walter Scott    16 年前

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

        17
  •  3
  •   CoolDude    13 年前

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

        18
  •  3
  •   Kim Ryan    10 年前

    这类问题很难解决,因为数据中存在潜在的歧义。

    下面是一个基于Perl的解决方案,它基于正则表达式定义递归下降语法树,以解析许多有效的街道地址组合: http://search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua/EN/AddressParse.pm . 这包括地址中的子属性,例如: 12 1st Avenue N Suite 2 Towere CA 12345 USA(美国)

    它类似于 http://search.cpan.org/~timb/Geo-StreetAddress-US-1.03/US.pm 以上提到,但也适用于非美国地址,如英国、澳大利亚和加拿大。

    这是您的一个示例地址的输出。请注意,需要首先从“A.P.Croll&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'
    
        19
  •  2
  •   anand    14 年前

    由于Word中存在错误的可能性,请考虑使用Soundex和LCS算法来比较字符串,这将有很大帮助!

        20
  •  2
  •   Andrew Barber Eric Lafortune    12 年前

    使用谷歌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);
    
        21
  •  2
  •   Sachin Prasad    11 年前

    对于Ruby或Rails开发人员来说,有一个很好的gem叫做 street_address . 我在我的一个项目中使用过这个,它完成了我需要的工作。

    我唯一的问题是每当地址是这种格式时 P. O. Box 1410 Durham, NC 27702 它返回nil,因此我必须将“p.o.box”替换为“”,然后它就能够解析它了。

        22
  •  1
  •   AdamSane    16 年前

    有些数据服务提供了一个邮政编码,它会给你该邮政编码中的街道名称列表。

    使用regex提取zip或city state-找到正确的一个,或者如果一个错误同时得到这两个。 将街道列表从 data source 更正城市和州,然后更正街道地址。一旦获得有效的地址行1、城市、州和邮政编码,就可以对地址行2..3进行假设。

        23
  •  1
  •   Shawn    14 年前

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

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

        24
  •  1
  •   hassansin    10 年前

    有perl geo::streetaddress::us包的javascript端口: https://github.com/hassansin/parse-address . 它是基于regex的,工作得相当好。