在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/ }
源代码中的这些文件可以提供更多细节:
- https://github.com/daveworth/Indirizzo/blob/master/lib/indirizzo/address.rb
- https://github.com/daveworth/Indirizzo/blob/master/lib/indirizzo/constants.rb
- https://github.com/daveworth/Indirizzo/blob/master/lib/indirizzo/numbers.rb
(但我也普遍同意@ 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]
或者如果你希望你的正则表达式更准确,你可以用(?
替换(?
(?
并添加所有不同的选项”我想要
您可以使用以下内容:
^\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(”)