• 售前

  • 售后

热门帖子
入门百科

PHP智能辨认收货所在信息实例

[复制链接]
程狄矢 显示全部楼层 发表于 2021-10-26 13:40:32 |阅读模式 打印 上一主题 下一主题
功能需求:用户输入混合的收货地点,能智能识别出地点,手机,姓名
准备:必要两张表,一张地域表和一张姓氏表 (地域表得到应该不难,姓氏表我是搜索中国姓氏自制的哈,底部会附上表结构)
思路:紧张思路分两种,一种是用户正常输入全地点,则序次按地域等级匹配地点;另一种用户非正常输入(省市区有缺少的),则全面模糊搜索表,再根据效果对比原地点。
提醒:手机可以根据本身需求修改正则;
名字只匹配中文,可以根据本身的需求修改姓氏表以及正则
地点匹配暂无发现问题
效果图:

代码:
  1. <?php
  2. class DistinguishAddress {
  3. /**
  4. * 类的入口方法
  5. * 传入地址信息自动识别,并返回最高匹配结果
  6. * 如果地址新增,则需要删除缓存文件重新缓存
  7. * @param $address
  8. **/
  9. function getAddressResult($address){
  10. // 优先第一种方法
  11. $result = $this->getAddressArrar($address);
  12. // 如果结果不理想,再模糊去匹配
  13. if($result['level'] != 3){
  14.   $result_sub = $this->addressVague($address);
  15.   // 只有全匹配对才替换,否则不做任何改变
  16.   if($result_sub['level'] == 3){
  17.    $result = $result_sub;
  18.   }
  19. }
  20. // 联系方式-优先匹配电话
  21. if(preg_match('/1\d{10}/', $address, $mobiles)){ // 手机
  22.   $result['mobile'] = $mobiles[0];
  23. } else if(preg_match('/(\d{3,4}-)?\d{7,8}/', $address, $mobiles)){ // 固定电话
  24.   $result['mobile'] = $mobiles[0];
  25. }
  26. // 识别姓名-必须空格分享的--概率
  27. preg_match_all('/[\x{4e00}-\x{9fa5}]{2,}/iu', $address,$names);
  28. if($names){
  29.   $name_where = '';
  30.   foreach ($names[0] as $name){
  31.    // 必须是大于1个字符且小于5个字符的
  32.    if(1 < mb_strlen($name,'utf-8') && mb_strlen($name, 'utf-8') < 5){
  33.     $sub_name = mb_substr($name, 0, 1, 'utf-8');
  34.     $name_where .= "name like '{$sub_name}%' or ";
  35.    }
  36.   }
  37.   if(!empty($name_where)){
  38.    $name_where = substr($name_where, 0, -3);
  39.    $names_sql = "select name from surname where {$name_where} order by sort desc";
  40.    $list = Db::getInstance('DbTrade')->getAll($names_sql);
  41.    // 统计有多少种可能性-姓名
  42.    $result['name_num'] = count($list);
  43.    if($list) {
  44.     $name_first = $list[0]['name'];
  45.     foreach ($names[0] as $name){
  46.      $len = mb_strlen($name_first, 'utf-8');
  47.      if (mb_substr($name, 0, $len, 'utf-8') == $name_first){
  48.       $result['name'] = $name;
  49.      }
  50.     }
  51.    }
  52.   }
  53. }
  54. // 去掉详细里面的姓名和电话
  55. $result['info'] = str_replace($result['mobile'], '', $result['info']);
  56. $result['info'] = str_replace($result['name'], '', $result['info']);
  57. $result['info'] = $result['province']['region_name'] . $result['city']['region_name'] . $result['district']['region_name'] . $result['info'];
  58. return $this->getCityLevelList($result);
  59. }
  60. /**
  61. * 获取对应城市等级列表
  62. **/
  63. function getCityLevelList($result){
  64. // 获取所有地址递归列表
  65. $regions = $this->getRegionTreeList();
  66. // 获取省份列表- 只有存在值才返回对应列表
  67. $province_id = $result['province']['region_id'];
  68. if ($province_id) {
  69.   foreach ($regions as $region){
  70.    unset($region['childs']);
  71.    $result['province_list'][] = $region;
  72.   }
  73. }
  74. // 获取城市列表- 只有存在值才返回对应列表
  75. $city_id = $result['city']['region_id'];
  76. if ($city_id) {
  77.   foreach ($regions[$province_id]['childs'] as $region){
  78.    unset($region['childs']);
  79.    $result['city_list'][] = $region;
  80.   }
  81. }
  82. // 获取地区列表- 只有存在值才返回对应列表
  83. $district_id = $result['district']['region_id'];
  84. if ($district_id) {
  85.   foreach ($regions[$province_id]['childs'][$city_id]['childs'] as $region){
  86.    unset($region['childs']);
  87.    $result['district_list'][] = $region;
  88.   }
  89. }
  90. return $result;
  91. }
  92. /**
  93. * 获取所有地址递归列表
  94. **/
  95. function getRegionTreeList(){
  96. // IO
  97. $file_name = 'regions.json';
  98. if(is_file($file_name)){
  99.   $regions = file_get_contents($file_name);
  100.   $regions = json_decode($regions, true);
  101. } else {
  102.   $region_sql = "select region_id,region_name,parent_id from region";
  103.   $regions = Db::getInstance('DbTrade')->getAll($region_sql);
  104.   $regions = $this->arrayKey($regions);
  105.   file_put_contents($file_name, json_encode($regions));
  106. }
  107. return $regions;
  108. }
  109. /**
  110. * 第一种方法
  111. * 根据地址列表递归查找准确地址
  112. * @param $address
  113. * @return array
  114. **/
  115. function getAddressArrar($address){
  116. // 获取所有地址递归列表
  117. $regions = $this->getRegionTreeList();
  118. // 初始化数据
  119. $province = $city = $district = array();
  120. // 先查找省份-第一级地区
  121. $province = $this->checkAddress($address, $regions);
  122. if($province){
  123.   // 查找城市-第二级地区
  124.   $city = $this->checkAddress($address, $province['list']);
  125.   if($city){
  126.    // 查找地区-第三级地区
  127.    // 西藏自治区那曲市色尼区辽宁南路西藏公路 第三个参数因为这个地址冲突取消强制
  128.    $district = $this->checkAddress($address, $city['list']);
  129.   }
  130. }
  131. return $this->getAddressInfo($address, $province, $city, $district);
  132. }
  133. /**
  134.   * 第二种方法
  135.   * 地址模糊查找
  136.   **/
  137. function addressVague($address){
  138. $res = preg_match_all('/\S{2}[自市区镇县乡岛州]/iu', $address,$arr);
  139. if(!$res) return false;
  140. $where = ' where ';
  141. foreach ($arr[0] as $value){
  142.   if(strpos($value, '小区') === false && strpos($value, '开发区') === false){
  143.    $where .= "region_name like '%{$value}' or ";
  144.   }
  145. }
  146. $where = substr($where,0,-3);
  147. $region_sql = "select region_id,region_name,parent_id,region_type from region " . $where;
  148. $citys = $GLOBALS['db']->getAll($region_sql);
  149. // 匹配所有地址
  150. $result = array();
  151. foreach ($citys as &$city){
  152.   // 所有相关联的地区id
  153.   $city_ids = array();
  154.   if($city['region_type'] == 2) {
  155.    $city_ids = array($city['parent_id'], $city['region_id']);
  156.    // 尝试能不能匹配第三级
  157.    $region_sql = "select region_id,region_name,parent_id,region_type,left(region_name,2) as ab_name from region where parent_id='{$city['region_id']}'" ;
  158.    $areas = $GLOBALS['db']->getAll($region_sql);
  159.    foreach ($areas as $row){
  160.     if(mb_strpos($address,$row['ab_name'])){
  161.      $city_ids[] = $row['region_id'];
  162.     }
  163.    }
  164.   } else if($city['region_type'] == 3){
  165.    $region_sql = "select parent_id from region where region_id='{$city['parent_id']}'" ;
  166.    $city['province_id'] = $GLOBALS['db']->getOne($region_sql);
  167.    $city_ids = array($city['parent_id'], $city['region_id'], $city['province_id']);
  168.   }
  169.   // 查找该单词所有相关的地区记录
  170.   $where = " where region_id in(" . join(',', $city_ids) . ")";
  171.   $region_sql = "select region_id,region_name,parent_id,region_type,left(region_name,2) as ab_name from region " . $where . ' order by region_id asc';
  172.   $city_list = $GLOBALS['db']->getAll($region_sql);
  173.   sort($city_ids);
  174.   $key = array_pop($city_ids);
  175.   $result[$key] = $city_list;
  176.   sort($result);
  177. }
  178. if($result){
  179.   list($province, $city, $area) = $result[0];
  180.   return $this->getAddressInfo($address, $province, $city, $area);
  181. }
  182. return false;
  183. }
  184. /**
  185. * 匹配正确的城市地址
  186. * @param $address
  187. * @param $city_list
  188. * @param int $force
  189. * @param int $str_len
  190. * @return array
  191. **/
  192. function checkAddress($address, $city_list, $force=false, $str_len=2){
  193. $num = 0;
  194. $list = array();
  195. $result = array();
  196. // 遍历所有可能存在的城市
  197. foreach ($city_list as $city_key=>$city){
  198.   $city_name = mb_substr($city['region_name'], 0, $str_len,'utf-8');
  199.   // 判断是否存包含当前地址字符
  200.   $city_arr = explode($city_name, $address);
  201.   // 如果存在相关字眼,保存该地址的所有子地址
  202.   if(count($city_arr) >= 2){
  203.    // 必须名称长度同时达到当前比对长度
  204.    if(strlen($city['region_name']) < $str_len){
  205.     continue;
  206.    }
  207.    $num ++;
  208.    $list = $list + $city['childs'];
  209.    $result[] = array(
  210.     'region_id' => $city['region_id'],
  211.     'region_name' => $city['region_name'],
  212.     'list' =>$list,
  213.    );
  214.   }
  215. }
  216. // 如果有多个存在,则加大字符匹配长度
  217. if($num > 1 || $force){
  218.   $region_name1 = $result[0]['region_name'];
  219.   $region_name2 = $result[1]['region_name'];
  220.   if(strlen($region_name1) == strlen($region_name2) && strlen($region_name1) == $str_len){
  221.    $region_id1 = $result[0]['region_id'];
  222.    $region_id2 = $result[1]['region_id'];
  223.    $index = $region_id1 > $region_id2 ? 1 : 0;
  224.    $result = $result[$index];
  225.    return $result;
  226.   }
  227.   return $this->checkAddress($address, $city_list, $force, $str_len+1);
  228. } else {
  229.   $result[0]['list'] = $list;
  230.   return $result[0];
  231. }
  232. }
  233. /**
  234. * 根据原地址返回详细信息
  235. * @param $address
  236. * @param $province
  237. * @param $city
  238. * @param $area
  239. * @return array
  240. **/
  241. function getAddressInfo($address, $province, $city, $district){
  242. // 查找最后出现的地址 - 截取详细信息
  243. $find_str = '';
  244. if($province['region_name']){
  245.   $find_str = $province['region_name'];
  246.   if($city['region_name']){
  247.    $find_str = $city['region_name'];
  248.    if($district['region_name']){
  249.     $find_str = $district['region_name'];
  250.    }
  251.   }
  252. }
  253. // 截取详细的信息
  254. $find_str_len = mb_strlen($find_str,'utf-8');
  255. for($i=0; $i<$find_str_len-1; $i++){
  256.   $substr = mb_substr($find_str,0,$find_str_len - $i, 'utf-8');
  257.   $end_index = mb_strpos($address, $substr);
  258.   if ($end_index){
  259.    $address = mb_substr($address, $end_index + mb_strlen($substr) , mb_strlen($address) - $end_index);
  260.   }
  261. }
  262. !empty($find_str) && $find_str = '|\S*' . $find_str;
  263. $area['info'] = preg_replace("/\s*|,|,|:|:{$find_str}/i", '', $address);
  264. $level = 0;
  265. if($district['region_name']){
  266.   $level = 3;
  267. } else if($city['region_name']){
  268.   $level = 2;
  269. } else if ($province['region_name']) {
  270.   $level = 1;
  271. }
  272. return array(
  273.   'province' => array('region_id'=>$province['region_id'], 'region_name'=>$province['region_name']),
  274.   'city'  => array('region_id'=>$city['region_id'], 'region_name'=>$city['region_name']),
  275.   'district'  => array('region_id'=>$district['region_id'], 'region_name'=>$district['region_name']),
  276.   'info'  => $area['info'],
  277.   'level'  => $level,
  278. );
  279. }
  280. /**
  281. * 递归所有地址成无限分类数组
  282. * @param $data
  283. * @param int $region_id
  284. * @return array
  285. **/
  286. function arrayKey($data, $region_id=1){
  287. $result = array();
  288. foreach ($data as $row){
  289.   if($region_id == $row['parent_id']){
  290.    $key = $row['region_id'];
  291.    $row['childs'] = $this->arrayKey($data, $row['region_id']);
  292.    $result[$key] = $row;
  293.   }
  294. }
  295. return $result;
  296. }
  297. }
  298. ?>
复制代码
姓氏surname表(id,姓,优先匹配序次)
  1. DROP TABLE IF EXISTS `surname`;
  2. CREATE TABLE `surname` (
  3. `id` int(11) NOT NULL AUTO_INCREMENT,
  4. `name` char(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  5. `sort` int(11) NULL DEFAULT NULL,
  6. PRIMARY KEY (`id`) USING BTREE,
  7. INDEX `name`(`name`) USING BTREE,
  8. INDEX `sort`(`sort`) USING BTREE
  9. ) ENGINE = InnoDB AUTO_INCREMENT = 481 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '姓氏表' ROW_FORMAT = Compact;
复制代码
地点region表()
  1. CREATE TABLE `region` (
  2. `region_id` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `parent_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0,
  4. `region_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  5. `region_type` tinyint(1) NOT NULL DEFAULT 2,
  6. `agency_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0,
  7. PRIMARY KEY (`region_id`) USING BTREE,
  8. INDEX `parent_id`(`parent_id`) USING BTREE,
  9. INDEX `region_type`(`region_type`) USING BTREE,
  10. ) ENGINE = InnoDB AUTO_INCREMENT = 3956 AVG_ROW_LENGTH = 44 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
复制代码
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习大概工作具有一定的参考学习代价,谢谢大家对草根技能分享的支持。假如你想相识更多相干内容请查看下面相干链接

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作