6

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

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

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

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

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

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

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

4

5 回答 5

11

如果可能,我建议为此使用库,因为地址解析可能很困难。查看Indirizzo 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_Typefrom constants.rb),如下所示:

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”替代项,因为无论如何我们都丢弃了后缀。)

于 2013-04-21T18:52:23.713 回答
2

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

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

或者,如果您希望您的正则表达式更准确地使用您可以替换的类型 (?<type>.*)(?<type>(Blvd|Ave|Rd|St)) 添加您想要的所有不同选项

于 2013-05-02T19:35:54.443 回答
1

你也许可以使用类似的东西:

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

\S匹配任何非空白字符

^匹配字符串的开头

$匹配字符串的结尾

(.+?)捕捉两者之间的任何东西。

于 2013-04-21T18:53:38.800 回答
0

仔细检查您的数据集,以确保尚未为您处理此问题。

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

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


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

我遍历了所有地址,并从每个地址中提取了最后一个“单词”并检查了这个数组并提取了任何不是“Lane”、“Road”、“Rd”或其他任何“单词”。从这个地址结尾列表中,我创建了这个巨大的匹配正则表达式对象

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

我遍历了每个地址字符串,shift找出了第一个数组成员,因为那几乎总是街道号码。然后 gsub'd 出街道结尾以获得应该是没有街道号码或街道名称结尾的街道名称,数据库通常不喜欢这些:

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

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

于 2013-09-25T18:13:16.390 回答
0

我目前只是将我得到的任何东西传递给 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');
}
});
}

在那之后,我只是把它分成红宝石。使用 .split(', ') 和 .split(' ')

于 2016-05-26T17:38:56.957 回答