从字符串中提取地址

4

假设我有这个字符串:

<div>john doe is nice guy btw 8240 E. Marblehead Way 92808  is also</div>

或者这个字符串:
<div>sky being blue? in the world is true? 024 Brea Mall  Brea, California 92821 jackfroast nipping on the firehead</div>

我该怎么从这些字符串中提取地址?这可能涉及到某种正则表达式,对吗?
我尝试在网上寻找使用JavaScript或PHP的解决方案,但都没有成功。 至今为止,Stack Overflow上的任何其他文章都没有提供使用jQuery和/或Javascript和/或PHP的解决方案。最接近的是Parse usable Street Address, City, State, Zip from a string,但该主题中没有关于从字符串中提取邮政编码的代码。
有人能给我指点方向吗?我该如何在jQuery或JavaScript或PHP中完成这个任务?

2
看起来这是正则表达式的一个案例。尽管如此,因为我对你的动机持有质疑态度,所以我依然不会帮助你。 - Philipp
3
@Philipp 是什么动机?!? - user752723
1
你需要一组严格的正则表达式过滤器来验证地址,无论你使用哪种语言...祝你好运...这不是一件简单的事情! - charlietfl
1
@Philipp 等等,什么?我需要解析地址来为我的提醒服务做准备!这是我正在开发的网址:http://dumbsearch.com/now2.php 当人们输入提醒时,我想要检测地址,这样当日期到来时,它将显示提醒以及到达那里需要多少分钟,并提供一个链接到苹果地图。这是一个iPhone的Web应用程序,但它也可以在桌面上使用。试试看!我其他大部分问题都与此相关!比如看看http://stackoverflow.com/questions/14014619/simplexml-not-returning-anything!在问题中,我问我的MapQuest API不起作用。 - user752723
1
@Philipp 你为什么删除了你的评论? - user752723
显示剩余3条评论
6个回答

23

我尝试了12个与你的类似的字符串,它完美地工作了:

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;
}

这里假设房屋号码至少有两位数,但不超过六位数。同时也假设邮政编码不是“扩展”形式(例如12345-6789)。然而,这可以很容易地修改以适应该格式(在此处使用正则表达式是一个不错的选择,例如(\d{5}-\d{4}))。
但是,在解析用户输入数据时使用正则表达式并不是一个好主意,因为我们不知道用户将输入什么,因为没有任何验证(可以想象)。
从代码和逻辑开始,首先从上下文中创建数组并获取邮政编码:
// 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;

4
哇!感谢您的回复!非常全面!!非常详细!太好了!(顺便说一下,我授予了您100点赏金,所以现在您的声誉为+100:))我还将您的答案标记为正确,并进行了投票。它适用于所有测试,无论字符串中是否有其他数字! :) - user752723
2
非常感谢您的精彩回复! - user752723
2
没有,你太聪明了!我在任何地方都找不到类似的脚本! - user752723
2
有时候最好的解决方案往往是最简单的。 :) - Josh Brody
1
:) 嗯,再次感谢。 :) 很高兴我能给你 +100 的声望值。 :) - user752723
你能否也用JavaScript发布这段代码吗?@Josh Brody - Lakshmi

2
如果您的地址始终以数字开头和结尾,您可以使用此正则表达式提取所需数据:
/[0-9].+[0-9]/gi

Javascript示例:

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

但是这只有当你每行都有匹配信息时才有用。如果你真的需要一个强大的地址匹配器,你需要继续前进并创建强大的分析工具。
你可以在文本中搜索目标关键字,然后过滤段落,接着剥离出你所寻找的信息。
这不是一个简单的问题,但是可以做到。你可以使用多个正则表达式来进行一些匹配,但是如果地址没有模式,那么正则表达式就会无用,这时你需要改变你的方法。

谢谢,但这不起作用,因为字符串中还会有电话号码... :( - user752723
请提供一个例子,您也可以将“+”更改为“*”,以获取到最后一个数字值,这样就可以获取所有行内的数字。 - Gabriel Gartz
1
非常感谢您的帮助,顺便说一下。 :) 这里是一个例子:"John Doe 7143138656 位于 8240 E Marblehead Way 92808"。 - user752723

2

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

所有地址都必须按照此格式。您不能仅仅从字符串中提取地址。


但是有人能提供一个查找这个的样本正则表达式吗?(数字,文本,以5位数字结尾) - user752723
这个线程:https://dev59.com/uXVD5IYBdhLWcg3wU5-H 提供了一些好的指针,可以找到哪些匹配项...但我想要一个示例正则表达式代码来实现这个...谢谢!(PS你得到了我的赞) - user752723

1

由于方便,尝试使用正则表达式解析所有内容是一个常见的“错误”。然而,正则表达式并不是万能的。在这种情况下,看起来你不是在寻找文本中的正则模式,而是“自然”的表达方式,就像有人在和你交谈一样。这些自然的表达方式不一定会遵循任何一致的模式。有些人会先写公寓号码,然后是建筑物号码,有些人会省略城市直接跳到邮政编码,有些人可能会先写城市、州、国家,然后才是邮政编码。不可能列举出每个可能的正则表达式模式,以便解析地址。

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

我建议从左向右逐个单词地阅读文本(至少在英语中)。每个单词都需要进行一次逻辑测试,即“这个单词可能是地址的起始位置吗?”我假设这是一个数字,可以是建筑物编号或公寓/单元/箱子编号(例如“Box XXX”,“PO BOX XXX”,“PO XXX”,“Unit XXX”,“#XXX”或任何长度小于6位数的数字)。虽然我不知道这是否属实,但我从未见过一个北美建筑物编号长度为7位数,而电话号码的最小长度为7位数。因此,我认为您可以相当容易地区分电话号码和建筑物编号。这个“地址起始位置”测试可以是一组正则表达式匹配,但我们不匹配整个地址,只是测试开始地址的单词或短语。我甚至认为,如果没有使用正则表达式匹配,它会更简单。
一旦检测到地址的起始位置,您就创建一个“地址解析状态对象”(一些类用于保存地址,继续解析并跟踪已有内容以及接下来预期的内容)。现在,您可以继续遍历句子并继续添加到解析状态对象中。在建筑物编号后面,我可能会期望街道名称或方向指示器(N.E.W.S. NE.NW.SE.SW.)。如果接下来没有这两个内容之一,请停止地址解析并假设地址无效或不完整,继续寻找新的地址起始词。否则,将街道名称和/或方向指示器添加到解析树中,并继续进行!
跟随街道名称的任何内容都可能是无限变化的。一些用户可能只在建筑物编号和街道名称处停止(假定他们所在的城市/地区/国家)。否则,您可能正在寻找城市名称或邮政编码/邮政编码。如果找到,则添加到地址解析状态对象中;否则,假定地址不完整(填充用户默认位置信息?)或无效地址(忽略并继续寻找另一个地址起始位置?)。
最终,这种方法可能只需要一个相当简单的JavaScript方法,也许有几百行代码(我不是PHP专家,但我认为它应该类似)。如果您尝试枚举每个可能的正则表达式模式,某人可以构造一个地址,那么仅这些就会有数百个,而且仍然不可靠!(如果您尝试匹配数百个正则表达式模式,那么可能还很慢)。

谢谢您的回复! :) 您能否指向一个预制的PHP解析算法,最适合这个问题?或者您能为我编写一个基本的JavaScript示例代码吗?谢谢。 :) - user752723
@DumbProducts 很高兴这对你有帮助。我认为这个网站的性质更多是帮助你自己完成,提供一些战略性的帮助和指导。如果你想让我直接为你编写代码,请随意点击我的个人资料,找到我的日常工作并购买一些咨询时间。这需要几个小时的工作量,我不会在这里免费完成。我也要吃饭啊。 :-) - BenSwayne

0

我在编程中使用Google Geocode API时运气最佳。它消除了尝试考虑每种可能的地址字符串输入方式的困难。

最近,我需要从单个字符串中提取地址的部分,用于房地产网站,我发现最好的选择是使用Google Geocode API。它允许我获取每个输入地址的街道、城市、州、邮政编码、纬度、经度等信息。

我在这里找到了一个很棒的指南,可以帮助你设置Google Geocode API(PHP):http://www.andrew-kirkpatrick.com/2011/10/google-geocoding-api-with-php/

最好的部分是,它甚至适用于地名。因此,搜索“UCLA”或“苹果总部”将为您提供可能需要的所有地址部分。


-1

我的想法是你应该有一些方法告诉你的代码,“从这里到这里是一个地址,其余部分是简单文本”。为此,你可以创建一个地址数组或将地址保存在数据库中,然后与插入的值进行比较。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接