在Ruby中解析街道地址

我正在将地址处理为它们各自的数据库字段格式。 我可以得到房屋号码和街道类型,但试图确定最好的方法来获得没有数字和最后一个字的街道。 收到的标准街道地址是:

res[:address] = '7707 Foo Bar Blvd' 

截至目前,我可以解析以下内容:

  house = res[:address].gsub(/\D/, '') street_type = res[:address].split(/\s+/).last 

我的第一个挑战是如何获得’Foo Bar’。 请注意,街道名称可以是一个,两个或三个单词。 我正在努力为Ruby找到一个单行表达式解决方案。

我的第二个问题是如何改进“房子”代码来处理最后有alpha的门牌号码。 例如,“7707B”。

最后,如果您可以参考一个好的备忘单,其中包含有助于这些表达的示例。

如果可能的话,我建议使用一个库,因为地址解析可能很困难。 查看Indirizzo Rubygem, 简化 :

 require 'Indirizzo' address = Indirizzo::Address.new("7707 Foo Bar Blvd") address.number => "7707" address.street => ["foo bar blvd", "foo bar boulevard"] 

即使您使用Indirizzo库本身,阅读其源代码可能非常有用,看看他们如何解决问题。 例如,它精确调整了正则表达式以匹配地址的不同部分:

 Match = { # FIXME: shouldn't have to anchor :number and :zip at start/end :number => /^(\d+\W|[az]+)?(\d+)([az]?)\b/io, :street => /(?:\b(?:\d+\w*|[a-z'-]+)\s*)+/io, :city => /(?:\b[az][a-z'-]+\s*)+/io, :state => State.regexp, :zip => /\b(\d{5})(?:-(\d{4}))?\b/o, :at => /\s(at|@|and|&)\s/io, :po_box => /\b[P|p]*(OST|ost)*\.*\s*[O|o|0]*(ffice|FFICE)*\.*\s*[B|b][O|o|0][X|x]\b/ } 

源代码中的这些文件可以提供更多细节:

(但我也普遍同意@ drhenner的评论,为了让自己更容易,你可能只是在不同的领域接受这些数据输入。)

编辑:要提供有关如何删除街道后缀(例如“Blvd”)的更具体的答案,您可以使用Indirizzo的正则表达式常量(例如来自constants.rb Suffix_Type ),如下所示:

 address = Indirizzo::Address.new("7707 Foo Bar Blvd", :expand_streets => false) address.street.map {|street| street.gsub(Indirizzo::Suffix_Type.regexp, '').strip } => ["foo bar"] 

(注意我也传递了:expand_streets => false到初始化器,以避免扩展“Blvd”和“Boulevard”替代方案,因为我们无论如何都要丢弃后缀。)

您可以使用正则表达式中的命名捕获组快速和松散地进行游戏

 matches = res[:address].match(/^(?\S*)\s+(?.*)\s+(?.*)$/) number = matches[:number] house = matches[:name] street_type = matches[:type] 

或者如果你希望你的正则表达式更准确,你可以用(?(Blvd|Ave|Rd|St))替换(?.*) (?(Blvd|Ave|Rd|St))并添加所有不同的选项”我想要

您可以使用以下内容:

^\S+ (.+?) \S+$

\S匹配任何非空白字符

^匹配字符串的开头

$匹配字符串的结尾

并且(.+?)捕获两者之间的任何内容。

仔细检查数据集以确定是否尚未处理此问题。

我花了相当多的时间首先创建一个可能街道名称结尾的分类,使用regexp条件试图从完整的地址字符串和所有内容中取出街道号码,结果发现我的shapefile的属性表已经分段了这些组件。

在你继续解析地址字符串的过程之前,由于不可避免的奇怪变化(一些包裹地址用于内陆地块并且有奇怪的地址等),这总是有点苦差事,确保你的数据集还没有为你完成这个!!!


但如果不这样做,请运行地址字符串, address.split(" ")创建一个’words’数组。 在大多数情况下,第一个“单词”是街道号码。 这适用于我的地址的约95%。 (注意:我的:地址字符串不包含城市,县,州,邮编,它们只是本地地址)

我浏览了整个地址,并从每个地址中取出最后一个“单词”并检查了这个数组并拔出了不是“Lane”,“Road”,“Rd”或其他什么的“单词”。 从这个地址结尾列表中,我创建了这个巨大的匹配regexp对象

 streetnm_endings = street_endings.map {|s| /#{s}/ } endings_matches = Regexp.union(street_endings) 

我浏览了每个地址字符串, shift出第一个数组成员,因为这几乎总是街道号码。 然后gsub out the street endings得到街道名称sans街道号码或街道名称结尾,这些数据库通常不喜欢:

 parcels.each do |p| remainder = p.address.split(" ") p.streetnum = remainder.shift p.streetname = remainder.join(" ").gsub(endings_matches, "") p.save end 

它并不总是有效,但大部分时间都有效。

我目前只是通过我给予googlemaps的任何内容,并让他们发回一个非常容易解析的格式化的街道地址。

 function addressReview(addressInput) { geocoder = new google.maps.Geocoder(); var latlng = new google.maps.LatLng(-34.397, 150.644); geocoder.geocode( { 'address': addressInput}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { if (results[0]) { var addr = results[0].formatted_address; var latTi = results[0].geometry.location.lat(); var lonGi = results[0].geometry.location.lng(); $.post('/welcome/gcode',{ add: addr , la: latTi , lo: lonGi }); $('#cust_addy').val(addr); } else { $('#cust_addy').attr("placeholder",'Cannnot determine location'); } } else { $('#cust_addy').attr("placeholder",'Cannnot determine location'); } }); } 

在那之后,我把它分成ruby。 .split(’,’)和.split(”)