从字符串中提取地址

假设我有这个字符串:

john doe is nice guy btw 8240 E. Marblehead Way 92808 is also

或者这个字符串:

 
sky being blue? in the world is true? 024 Brea Mall Brea, California 92821 jackfroast nipping on the firehead

我如何从其中一个字符串中提取地址? 这会涉及某种正则表达式,对吧?

我试过在线寻找使用JavaScript或PHP的解决方案,但无济于事。 Stack Overflow上没有其他post(据我所知)提供了一个使用jQuery和/或Javascript和/或PHP的解决方案。 (最接近的是Parse可用街道地址,城市,州,Zip,来自字符串 ,其中没有关于从字符串中提取邮政编码的线程中的任何代码。

有人能指出我正确的方向吗? 我将如何在jQuery或JavaScript或PHP中完成此操作?

尝试了十二个与你的相似的不同字符串,它工作得很好:

 function str_to_address($context) { $context_parts = array_reverse(explode(" ", $context)); $zipKey = ""; foreach($context_parts as $key=>$str) { if(strlen($str)===5 && is_numeric($str)) { $zipKey = $key; break; } } $context_parts_cleaned = array_slice($context_parts, $zipKey); $context_parts_normalized = array_reverse($context_parts_cleaned); $houseNumberKey = ""; foreach($context_parts_normalized as $key=>$str) { if(strlen($str)>1 && strlen($str)<6 && is_numeric($str)) { $houseNumberKey = $key; break; } } $address_parts = array_slice($context_parts_normalized, $houseNumberKey); $string = implode(' ', $address_parts); return $string; } 

这假定门牌号至少为两位数,且不大于6位。 这也假定邮政编码不是“扩展”forms(例如12345-6789)。 然而,这可以很容易地修改以适应这种格式(正则表达式在这里是一个很好的选择,类似于(\d{5}-\d{4})

但是使用正则表达式来解析用户输入的数据......这里不是一个好主意,因为我们只是不知道用户将要输入什么,因为(可以假设)没有validation。

遍历代码和逻辑,从上下文创建数组并抓取zip:

 // split the context (for example, a sentence) into an array, // so we can loop through it. // we reverse the array, as we're going to grab the zip first. // why? we KNOW the zip is 5 characters long*. $context_parts = array_reverse(explode(" ", $context)); // we're going to store the array index of the zip code for later use $zipKey = ""; // foreach iterates over an object given the params, // in this case it's like doing... // for each value of $context_parts ($str), and each index ($key) foreach($context_parts as $key=>$str) { // if $str is 5 chars long, and numeric... // an incredibly lazy check for a zip code... if(strlen($str)===5 && is_numeric($str)) { $zipKey = $key; // we have what we want, so we can leave the loop with break break; } } 

做一些整理,以便我们有一个更好的对象来装饰房屋号码

 // remove junk from $context_array, since we don't // need stuff after the zip $context_parts_cleaned = array_slice($context_parts, $zipKey); // since the house number comes first, let's go back to the start $context_parts_normalized = array_reverse($context_parts_cleaned); 

然后让我们使用与我们执行邮政编码相同的基本逻辑来获取门牌号码:

 $houseNumberKey = ""; foreach($context_parts_normalized as $key=>$str) { if(strlen($str)>1 && strlen($str)<6 && is_numeric($str)) { $houseNumberKey = $key; break; } } // we probably have the parts we for the address. // let's do some more cleaning $address_parts = array_slice($context_parts_normalized, $houseNumberKey); // and build the string again, from the address $string = implode(' ', $address_parts); // and return the string return $string; 

正则表达式用于测试模式 。 你需要知道你正在寻找什么样的模式。 从您提供的两个示例中,我会查找一个数字,然后是一些文本,以五位数字结尾。

所有地址都必须采用这种格式。 你不能神奇地从字符串中提取地址。

如果你的所有地址都以数字开头和结尾,你可以使用这个正则表达式来提取你需要的数据:

 /[0-9].+[0-9]/gi 

Javascript示例:

 "
john doe is nice guy btw 8240 E. Marblehead Way 92808 is also
".match(/[0-9].+[0-9]/gi) // ["8240 E. Marblehead Way 92808"] "
sky being blue? in the world is true? 024 Brea Mall Brea, California 92821 jackfroast nipping on the firehead
".match(/[0-9].+[0-9]/gi) // ["024 Brea Mall Brea, California 92821"]

对于包含电话号码的新示例,您可以执行以下操作:

 /[0-9].*[0-9]/gi 

Javascript示例:

 "john doe 7143138656 is 8240 e marblehead way 92808".match(/[0-9].*[0-9]/gi) // ["7143138656 is 8240 e marblehead way 92808"] 

但是,只有每行都有匹配信息时,这才能帮助您。 如果你真的需要一个强大的地址匹配器,你将需要继续,并创建强大的分析。

您可以在文本中开始搜索目标关键字,然后过滤参数,然后去除您要搜索的信息。

这不是一个简单的问题,但可以做到,你可以使用多个正则表达式进行一些匹配,但如果地址没有模式,正则表达式将是无用的,那时你需要改变你的方法。

由于方便,尝试使用正则表达式解析所有内容是一种常见的“错误”。 但是,正则表达式并不能解决所有问题。 在这种情况下,它看起来并不像是在寻找文本中的常规模式,而是某些人会写的“自然”表达,就好像他们正在和你说话一样。 这些自然表达不一定遵循任何一致的模式。 有些人首先放置appt号码然后建立号码,有些人遗漏城市并跳过邮政编码,有些人可能会把城市,州,国家那么拉链。 只是不可能枚举某人可以使用地址烹饪的每个可能的正则表达式模式。

对于自然语言地址,我会忘记正则表达式地址检测并转向有状态解析算法。

  1. 我首先要从左到右(至少用英语)一次一个字地阅读文本。 在每个单词中,您将进行一次逻辑测试“这个词可能是地址的开头吗?”。 我认为这是建筑物编号或设备/单位/箱号的号码(所以“Box XXX”,“PO BOX XXX”,“PO XXX”,“Unit XXX”,“#XXX”或任何数字超过6位数)。 虽然我不知道这是事实上的真实,但我从来没有见过长度为7位数的北美建筑物,这是手机的最小值。 所以我怀疑你可以很容易地找出电话号码与建筑物号码。 这个“地址开始”测试可能是一组正则表达式匹配,但我们不匹配整个地址,只测试启动地址的单词或短语。 我甚至可能会说没有正则表达式匹配更简单

  2. 一旦检测到地址的开始,就会创建一个“地址解析状态对象”(用于保存地址的某个类,用于继续解析并跟踪到目前为止的内容和接下来的内容)。 现在,您可以继续单步执行句子并继续添加到解析器状态对象。 根据建筑物编号,我可能期望街道名称或方向指示器(NEWS NE.NW.SE.SW。)。 如果下一个都没有停止您的地址解析并假设地址无效或不完整,请继续寻找新的地址字开头。 否则将街道名称和/或方向指示符添加到您的解析树并继续!

  3. 街道名称后面的任何内容都可以无限变化。 一些用户可能只是停在建筑物编号和街道名称(假设他们的当地城市/地区/国家)。 否则,您可能正在寻找城市名称或邮政编码/邮政编码。 如果找到,添加到您的地址解析状态对象,如果不假设不完整的地址(填写用户默认位置信息?)或无效地址(忽略并继续寻找另一个地址开始?)。

最终这种方法可能是一种相当简单的JavaScript方法,可能有几百行代码(我不是PHP人,但我认为它类似)。 如果你试图列举所有可能的正则表达式模式,有人可以构建一个地址,你只有数百个,它仍然是不可靠的! (如果你试图匹配数百个正则表达式模式,也可能会变慢)。

我的想法是你应该告诉你的代码“在这里形成一个地址,其余的是简单的文本”。 为此,您可以创建一个地址数组,也可以将地址保存在数据库中,以便将其与插入的值进行比较

我使用Google Geocode API获得了最好的运气。 尝试考虑地址字符串可能输入的每种可能方式都很困难。

我最近不得不从房地产网站的单个字符串中提取地址的一部分,我发现最好的选择是使用谷歌地理编码API。 它允许我为输入的每个地址获取街道,城市,州,邮编,纬度,经度等。

我在这里找到了一个很好的指南来设置谷歌地理编码API(PHP): http : //www.andrew-kirkpatrick.com/2011/10/google-geocoding-api-with-php/

最好的部分,甚至可以使用地名。 因此,搜索“加州大学洛杉矶分校”或“Apple总部”将为您提供您可能需要的地址的所有部分。