实现类似百度的全文检索高亮语法显示的自定义PHP函数

#1 cygsd

先看效果,原字符串:
 
while 语句的含意很简单,它告诉 PHP 只要 while 表达式的值为 TRUE 就重复执行嵌套中的循环语句。表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行,直到本次循环结束。有时候如果 while 表达式的值一开始就是 FALSE,则循环语句一次都不会执行。如果调用时不带可选参数,本函数以的格式返回一个字符串,其中 sec 是自 Unix 纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数,msec 是微秒部分。字符串的两部分都是以秒为单位返回的。手册首页仅突出了目前最活跃的人员,但是还有更多的贡献者在现在或者过去对本项目提供了大量的帮助。很多不留名的人也帮忙在手册中写下了笔记,并不断被包括在手册中,也很感谢他们的工作。下面的姓名列表按照字母顺序排列。


检索关键词,无次序要求:
 
Array ( [0] => 手册 [1] => 检查 [2] => 字符串 )


处理后的字符串,关键词语法高亮显示,前后各保留10个字符,其余内容用省略号表示,总长度设定为150个字符,有点百度的味道了。
如下:
... 的值在每次开始循环时检查,所以即使这个值在循环 ... 函数以的格式返回一个字符串,其中 sec 是自 ... sec 是微秒部分。字符串的两部分都是以秒为单位返回的。手册首页仅突出了目前最活跃 ... 多不留名的人也帮忙在手册中写下了笔记,并不断被包括在手册中,也很感谢他们 ...


PHP函数代码,说明和注释都在代码里:
 
/****************************************************************************
*** strKeyCut 关键词全文搜索语法显示函数
***
*** $rsstr  :待处理字符串
*** $keywords :关键词数组
*** $keycolor :是否语法高亮显示,默认=显示
*** $length  :指定字符串处理后长度,默认=200
*** $cutlen  :指定关键词前后保留文字长度,默认=10
****************************************************************************/
function strKeyCut($rsstr,$keywords,$keycolor=true,$length=200,$cutlen=10){
if (!$rsstr) return FALSE;
if (!is_array($keywords) or (1>count($keywords))) return FALSE;
$encoding=mb_internal_encoding();
mb_internal_encoding("UTF-8");//设置当前字符集为UTF8
$rsstr=preg_replace('/\s(?=\s)/', '', $rsstr); //连续的空格只保留一个
$pos=array();
foreach($keywords as $keys=>$words){ //查找关键词位置
  $offset=0;
  while($thepos=mb_strpos($rsstr,$words,$offset)){
   $pos[]=$thepos;
   $offset=$thepos+1;
  }
}
sort($pos); //关键词位置按先后排序
$strs=array();
$strs[]=mb_substr($rsstr,0,$pos["0"]); //按关键词的位置分割字符串
for ($i=0;$i<(count($pos)-1);$i++) $strs[]=mb_substr($rsstr,$pos[$i],$pos[$i+1]-$pos[$i]);
$strs[]=mb_substr($rsstr,$pos[$i]);
$outstr=array(); //对已分割的字符串长度进行处理
//第一个分割串中没有关键词,单独处理
$outstr[]=(($length>mb_strlen($rsstr)) or ($cutlen>mb_strlen($strs["0"]))) ? $strs["0"] : "... " . mb_substr($strs["0"],mb_strlen($strs["0"])-$cutlen);
for ($i=1;$i<(count($strs)-1);$i++){
  if (($cutlen+$cutlen+3)>=mb_strlen($strs[$i])) $outstr[]=$strs[$i];//如果关键词出现的位置较近,则不断句
  else{
   $lstr=mb_substr($strs[$i],0,$cutlen+3);//关键词后边保留一部分语句
   $rstr=mb_substr($strs[$i],mb_strlen($strs[$i])-$cutlen);//下一个关键词前面保留一部分语句
   $outstr[]=$lstr . " ... " . $rstr;
  }
}
$outstr[]=mb_substr($strs[$i],0,$cutlen);//处理最后一个分割串
$out= implode("",$outstr);//合并分割串
$out= mb_substr($out,0,$length) . " ...";  //按指定长度作最后裁切
if ($keycolor){
  foreach ($keywords as $key=>$data){
   $keycolors[$key]="".$data.""; //关键词修饰
  }
  $out=str_replace($keywords,$keycolors,$out);
}
mb_internal_encoding($encoding);//恢复字符集
return $out;
}


测试代码:
$keys=array("手册","检查","字符串");
$str="while 语句的含意很简单,它告诉 PHP 只要 while 表达式的值为 TRUE 就重复执行嵌套中的循环语句。表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行,直到本次循环结束。有时候如果 while 表达式的值一开始就是 FALSE,则循环语句一次都不会执行。如果调用时不带可选参数,本函数以的格式返回一个字符串,其中 sec 是自 Unix 纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数,msec 是微秒部分。字符串的两部分都是以秒为单位返回的。手册首页仅突出了目前最活跃的人员,但是还有更多的贡献者在现在或者过去对本项目提供了大量的帮助。很多不留名的人也帮忙在手册中写下了笔记,并不断被包括在手册中,也很感谢他们的工作。下面的姓名列表按照字母顺序排列。";
$out=strKeyCut($str,$keys,150);
echo "关键词:
";
print_r($keys);
echo "

原字符串:
" .$str;
echo "

处理后字符串:
" .$out;


附:获取用户输入关键词并进行处理的代码:
 
$str=$this->spArgs("keywors"); //$str中保存着用户输入的关键词
$rep=array("," , "." , "(" , ")" , "|" , "," , "。" , "<" , ">" , "?"); //非法字符过滤
$str=str_replace($rep," ",$str); //用空格替换非法字符
$str=trim($str);
$str=preg_replace('/\s(?=\s)/', '', $str); //去掉重复的空格
$keys=explode(" ",$str); //生成关键字数组


说明:本函数全部使用的是mb_系列PHP函数,所以只支持中文关键词搜索,英文关键词无效。盼高手指点一下,如何兼容中英文。

2010-11-06 14:09:12

#2 jake

1. 如果编码没问题的情况下,可以不用mb_strpos,而直接用strpos,就可以兼容中英文了。上面主要是因为要顾及到编码问题,所以才用mb_系列函数。
2. 这种功能的话,好像直接用正则也是可以实现的。
3. 在页面上高亮关键字,用JS来做是更好的选择,当然“全文搜索”还是得PHP来做。

2010-11-07 09:44:37