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/
}
源代码中的这些文件可以提供更多细节:
suffix_Type
fromconstants.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
fromconstants.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');
}
});
}