Ruby on rails 用Ruby解析街道地址

Ruby on rails 用Ruby解析街道地址,ruby-on-rails,ruby,regex,parsing,Ruby On Rails,Ruby,Regex,Parsing,我正在为数据库将地址处理为各自的字段格式。我可以得到房子的门牌号和街道类型,但我试图确定最好的方法来获得没有门牌号和最后一句话的街道。收到的标准街道地址为: res[:address] = '7707 Foo Bar Blvd' 到目前为止,我可以分析以下内容: house = res[:address].gsub(/\D/, '') street_type = res[:address].split(/\s+/).last 我的第一个挑战是如何获得“foobar”。

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

    res[:address] = '7707 Foo Bar Blvd'
到目前为止,我可以分析以下内容:

    house = res[:address].gsub(/\D/, '')
    street_type = res[:address].split(/\s+/).last
我的第一个挑战是如何获得“foobar”。请注意,街道名称可以是一个、两个或三个单词。我正努力在Ruby中找到一行表达式来解决这个问题

我的第二个问题是,如何改进“房屋”代码,以处理末尾有字母的房屋编号。例如,“7707B”


最后,如果你能参考一个很好的备忘单,上面有这些表达式的例子,那会很有帮助。

如果可能的话,我建议使用一个库来实现这一点,因为地址解析可能会很困难。查看Ruby gem,它使这变得简单:

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|[a-z]+)?(\d+)([a-z]?)\b/io,
  :street   => /(?:\b(?:\d+\w*|[a-z'-]+)\s*)+/io,
  :city     => /(?:\b[a-z][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的正则表达式常量(例如
suffix_Type
from
constants.rb
),如下所示:


(请注意,我还将
:expand_streets=>false
传递给初始值设定项,以避免同时扩展“Blvd”和“Boulevard”选项,因为我们正在丢弃后缀。)

您可能可以使用以下内容:

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

\S
匹配任何非空白字符

^
匹配字符串的开头

$
匹配字符串的结尾


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

您可以在正则表达式中快速和随意地使用命名的捕获组

matches = res[:address].match(/^(?<number>\S*)\s+(?<name>.*)\s+(?<type>.*)$/)
number = matches[:number]
house = matches[:name]
street_type = matches[:type]
matches=res[:address]。匹配(/^(?\S*)\S+(?*)\S+(?*)\S+(?*)$/)
number=匹配项[:number]
house=匹配项[:名称]
街道类型=匹配[:类型]
或者如果你想让你的正则表达式更精确一点,用你可以替换的类型
(?*)
具有
(?(大道|路|街))

并添加您想要的所有不同选项

仔细检查您的数据集,以确保此问题是否尚未为您解决

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

在继续解析地址字符串之前,由于不可避免地会出现奇怪的变化(有些包裹地址是内陆包裹的,并且有奇怪的地址等等),解析地址字符串总是有点麻烦,请确保您的数据集尚未为您完成此操作


但是如果您没有,请运行地址字符串,
address.split(“”
创建一个“单词”数组。在大多数情况下,第一个“单词”是街道号码。这对我95%的地址都有效。(注意:my:address字符串不包含城市、县、州、邮政编码,它们只是本地地址)

我浏览了所有地址,从每个地址中提取最后一个“单词”&检查这个数组并提取出任何不是“Lane”、“Road”、“Rd”之类的“单词”。从这个地址结尾列表中,我创建了这个巨大的匹配regexp对象

streetnm_endings = street_endings.map {|s| /#{s}/ }
endings_matches = Regexp.union(street_endings)
我遍历了每个地址字符串,
shift
-输出第一个数组成员,因为这几乎总是街道号码。然后,gsub将街道的尾端取出来,得到应该是什么街道名称,而不是街道编号或街道名称尾端,这通常是数据库不喜欢的:

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

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

我目前只需将我收到的任何信息传递给谷歌地图,让他们返回一个格式化的街道地址,这个地址很容易解析

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(“”)

简单,不要这样做!可以单独请求字段,也可以将其作为一个整体存储。解析它永远不会是100%准确的,因为变化量超出了您的计算范围。如果你想要一个门牌号字段(你不应该这样),那么在表单中有一个门牌号字段。顺便说一句:有一个带有USPS的API。他们可以验证你的地址,可能会给你更好的详细信息。我正在修改的基本属性数据库将字段以这种方式分开,这就是为什么字段房屋和街道类型。我还有一个完整的街道地址。我这样做是为了数据的一致性,在可能的情况下,我查看了这个gem,但我没有看到它在没有街道类型的情况下如何处理街道地址。同样,我们只需要处理匹配数据字段的问题。我也有一个完整的街道地址,只是为了让事情更容易理解。Rizzo似乎没有一个内置的方法将街道名称与其后缀(例如“Blvd”)分开。但它确实有这些后缀作为常量(例如
Indirizzo::Suffix_Type
from
constants.rb
。您可以使用这些来解析后缀。我已经更新了我的答案,并给出了一个如何做的建议。斯图尔特,我会尝试一下,但除了我以前做的之外,还需要一点时间来检查Gem。看起来这可能会奏效,也许我只需要sw勾勒出我以前的代码。将生成c
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');
}
});
}