开发者社区> 冰点沐雪> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

比较discuz和ecshop的截取字符串函数

简介: 网上看到一篇文章 discuz和ecshop截取字符串的两个函数,比较了一下两个版本的函数,都各有局限,只能在特定的前提下使用,但是学习一下有利于拓宽思路,了解PHP的扩展功能。   下面先给出两个版本函数的源代码以及简单测试,最后我会给出一个实用性更强的字符串截取函数。
+关注继续查看

网上看到一篇文章 discuz和ecshop截取字符串的两个函数,比较了一下两个版本的函数,都各有局限,只能在特定的前提下使用,但是学习一下有利于拓宽思路,了解PHP的扩展功能。

 

下面先给出两个版本函数的源代码以及简单测试,最后我会给出一个实用性更强的字符串截取函数。需要注意的是:这里讨论的字符串截取问题都是针对UTF-8编码的中文字符串。

 

discuz版本

复制代码
 1 /**
 2 * [discuz] 基于PHP没有安装 mb_substr 等扩展截取字符串,如果截取中文字则按2个字符计算
 3 * @param  $string     要截取的字符串
 4 * @param  $length     要截取的字符数
 5 * @param  $dot        替换截掉部分的结尾字符串
 6 * @return 返回截取后的字符串
 7 */
 8 function cutstr($string$length$dot = '...') {
 9   // 如果字符串小于要截取的长度则直接返回
10   // 此处使用strlen获取字符串长度有很大的弊病,比如对字符串“新年快乐”要截取4个中文字符,
11   // 那么必须知道这4个中文字符的字节数,否则返回的字符串可能会是“新年快乐...”
12   if (strlen($string) <= $length) {
13       return $string;
14   }
15   
16   // 转换原字符串中htmlspecialchars
17   $pre = chr(1);
18   $end = chr(1);
19   $string = str_replace ( array ('&amp;', '&quot;', '&lt;', '&gt;' ), array ($pre . '&' . $end$pre . '"' . $end$pre . '<' . $end$pre . '>' . $end ), $string );
20   
21   $strcut = '';   // 初始化返回值
22   
23   // 如果是utf-8编码(这个判断有点不全,有可能是utf8)
24   if (strtolower ( CHARSET ) == 'utf-8') {
25     // 初始连续循环指针$n,最后一个字位数$tn,截取的字符数$noc
26     $n = $tn = $noc = 0;
27     while ( $n < strlen ( $string ) ) {
28       $t = ord ( $string [$n] );
29       
30       if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
31         // 如果是英语半角符号等,$n指针后移1位,$tn最后字是1位
32         $tn = 1;
33         $n++;
34         $noc++;
35       } elseif (194 <= $t && $t <= 223) {
36         // 如果是二字节字符$n指针后移2位,$tn最后字是2位
37         $tn = 2;
38         $n += 2;
39         $noc += 2;
40       } elseif (224 <= $t && $t <= 239) {
41         // 如果是三字节(可以理解为中字词),$n后移3位,$tn最后字是3位
42         $tn = 3;
43         $n += 3;
44         $noc += 2;
45       } elseif (240 <= $t && $t <= 247) {
46         $tn = 4;
47         $n += 4;
48         $noc += 2;
49       } elseif (248 <= $t && $t <= 251) {
50         $tn = 5;
51         $n += 5;
52         $noc += 2;
53       } elseif ($t == 252 || $t == 253) {
54         $tn = 6;
55         $n += 6;
56         $noc += 2;
57       } else {
58         $n++;
59       }
60       
61       // 超过了要取的数就跳出连续循环
62       if ($noc >= $length) {
63         break;
64       }
65     }
66     
67     // 这个地方是把最后一个字去掉,以备加$dot
68     if ($noc > $length) {
69       $n -= $tn;
70     }
71     
72     $strcut = substr ( $string, 0, $n );
73   
74   } else {
75     // 并非utf-8编码的全角就后移2位
76     for ($i = 0; $i < $length$i ++) {
77       $strcut .= ord ( $string [$i] ) > 127 ? $string [$i] . $string [++ $i] : $string [$i];
78     }
79   }
80   
81   // 再还原最初的htmlspecialchars
82   $strcut = str_replacearray ($pre . '&' . $end$pre . '"' . $end$pre . '<' . $end$pre . '>' . $end ), array ('&amp;', '&quot;', '&lt;', '&gt;' ), $strcut );
83 
84   $pos = strrpos ( $strcutchr ( 1 ) );
85   if ($pos !== false) {
86     $strcut = substr ( $strcut, 0, $pos );
87   }
88   
89   return $strcut . $dot// 最后把截取加上$dot输出
90 }
复制代码

 

 discuz版本的最大缺陷在于使用 strlen 获取原始字符串的长度,并用来和传入的要截取长度参数(字节数)进行比较,由于UTF-8的中文字符的字节数是不固定的,所以就会面临这样的窘境:如果要截取4个中文字符应该指定多大的截取长度呢?8字节还是12字节呢?。。。这是无法预计的,也正是因为这个问题discuz的cutstr实际是有bug的,通过下面的测试结果能看出: 

$str1 = "欲穷千里目";
echo my_cutstr($str1, 10, "...")."\n";  // 输出:欲穷千里目...  [这是一个bug,想想是什么原因导致?]
echo my_cutstr($str1, 15, "...")."\n";  // 输出:欲穷千里目

  

导致上述bug的原因在与cutstr函数在截取字符的时候是将一个中文字按2个字符算,那么5个中文字就是10字符,而原始字符串的长度是15字节,所以cutstr认为“成功地”从15字符的串上截取了10个字符,然后加上了“尾巴”。要解决这个bug只要在判断一下返回的子串是否和原始串相同,如果相同就不加“尾巴”。

 

 

ecshop版

复制代码
 1 /**
 2 * [ecshop] 基于PHP的 mb_substr,iconv_substr 这两个扩展来截取字符串,中文字符都是按1个字符长度计算;
 3 * 该函数仅适用于utf-8编码的中文字符串。
 4 
 5 * @param  $str      原始字符串
 6 * @param  $length   截取的字符数
 7 * @param  $append   替换截掉部分的结尾字符串
 8 * @return 返回截取后的字符串
 9 */
10 function sub_str($str$length = 0, $append = '...') {
11     $str = trim($str);
12     $strlength = strlen($str);
13     
14     if ($length == 0 || $length >= $strlength) {
15         return $str;
16     } elseif ($length < 0) {
17         $length = $strlength + $length;
18         if ($length < 0) {
19             $length = $strlength;
20         }
21     }
22     
23     if ( function_exists('mb_substr') ) {
24         $newstr = mb_substr($str, 0, $length, 'utf-8');
25     } elseif ( function_exists('iconv_substr') ) {
26         $newstr = iconv_substr($str, 0, $length, 'utf-8');
27     } else {
28         //$newstr = trim_right(substr($str, 0, $length));
29         $newstr = substr($str, 0, $length);
30     }
31     
32     if ($append && $str != $newstr) {
33         $newstr .= $append;
34     }
35     
36     return $newstr;
37 }
复制代码

  

ecshop版的特点和缺点都在于将中文字符算作一个字符,如果原始字符串中不含中文,比如:abcd1234,如果本意是要截取4个中文字符或者8个英文字符,那么使用ecshop的版本就得不到期望的结果,返回值的是:abcd。下面是简单的测试结果: 

复制代码
$str1 = "白日依山尽,黄河入海流";
echo $str1."\n";
echo my_sub_str($str1, 4, "...")."\n";  // 输出:白日依山...
  
$str2 = "白1日2依3山4";
echo $str2."\n";
echo my_sub_str($str2, 4, "...")."\n";  // 输出:白1日2...
复制代码

 

 

优化版

截取中文字符串的大部分应用场景是“原始字符串可以是中文、英文、数字混杂的,中文字按2个字符算,英文数字按1个字符算”,针对这个需求下面给出一个实现版本:

 

复制代码
 1 /**
 2 * 字符串截取,中文字符按2个字符计算,同时支持GBK和UTF-8编码
 3 * @param  $string     要截取的字符串
 4 * @param  $length     要截取的字符数
 5 * @param  $append     添加到子串后的尾巴
 6 * @return 返回截取后的字符串
 7 */
 8 function substring($string$length$append = false) {
 9   if ( $length <= 0 ) {
10     return '';
11   }
12   
13   // 检测原始字符串是否为UTF-8编码
14   $is_utf8 = false;
15   $str1 = @iconv("UTF-8", "GBK", $string);
16   $str2 = @iconv("GBK", "UTF-8", $str1);
17   if ( $string == $str2 ) {
18     $is_utf8 = true;
19     
20     // 如果是UTF-8编码,则使用GBK编码的
21     $string = $str1;
22   }
23   
24   $newstr = '';
25   for ($i = 0; $i < $length$i ++) {
26     $newstr .= ord ($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
27   }
28   
29   if ( $is_utf8 ) {
30     $newstr = @iconv("GBK", "UTF-8", $newstr);
31   }
32   
33   if ($append && $newstr != $string) {
34         $newstr .= $append;
35     }
36     
37     return $newstr;
38 }
复制代码

 

 

测试结果见下(GBK和UTF-8的结果一致):

 

复制代码
$str1 = "白日依山尽,黄河入海流";
echo substring($str1, 4, "...")."\n";  // 输出:白日...
echo substring($str1, 5, "...")."\n";  // 输出:白日依...
  
$str2 = "12白34日56依78山";
echo substring($str2, 4, "...")."\n";  // 输出:12白...
echo substring($str2, 5, "...")."\n";  // 输出:12白3...
复制代码
 

 转载自: http://www.cnblogs.com/edwardlost/archive/2012/01/11/2318558.html

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
PHP:mb_substr中文字符串截取避免乱码
PHP:mb_substr中文字符串截取避免乱码
57 0
ecshop的缓存机制更改
ecshop是一个不错的平台,其中有一些地方可以改进一下 总体上看,就是一个大sql,global变量传来传去的,所有的只要取数据的时候,执行一个sql语句就可以了,但是一些需要缓存的地方,它使用的是文件缓存, 在这一点上,我觉得可以使用memcahced这个缓存机制来实现在includes中的init文件中加入以下代码 $mem = new Memcache; $mem->
798 0
MySQL之Double Write Buffer分析
之前有阅读过相关的文档和资料,总归差了点意思,这次抽出时间仔细推敲了一下,把一些结果记录下来; 杨大师的博文给了很大的帮助:http://blog.itpub.net/22664653/viewspace-1140915/-----------------------...
1216 0
***PHP各种编码的汉字字符串截取
虽然PHP有现成的截取字符串函数substr(),但是这个函数不能对汉字字符串进行截取,要实现这种效果还需要我们自己去编写相应的函数。汉字有多种编码,比如GB2312,UTF-8等,汉字字符串的截取需要区分这种汉字编码,下面是给出的几个解决方案。
712 0
php字符串比较函数
php字符串比较函数 www.111cn.net 编辑:Crese 来源:转载       比较两个字符串是否相等,最常见的方法就是使用“===”来判断,至于它和“==”的区别,简单来说就是前者强调“identical”类型也要求一样;后者要求“equal”,值相同就可以了,参考【1】。
1251 0
+关注
冰点沐雪
专业从事软件开发(asp.net)近10年。
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载