微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!
比较discuz和ecshop的截取字符串函数php版
下面先给出两个版本函数的源代码以及简单测试,最后我会给出一个实用性更强的字符串截取函数。需要注意的是:这里讨论的字符串截取问题都是针对UTF-8编码的中文字符串。
<div class="codetitle">
<a style="CURSOR: pointer" data="74781" class="copybut" id="copybut74781" onclick="doCopy('code74781')"> 代码如下: <div class="codebody" id="code74781">
/
[discuz] 基于PHP没有安装 mb_substr 等扩展截取字符串,如果截取中文字则按2个字符计算
@param $string 要截取的字符串
@param $length 要截取的字符数
@param $dot 替换截掉部分的结尾字符串
@return 返回截取后的字符串
/
function cutstr($string,$length,$dot = '...') {
// 如果字符串小于要截取的长度则直接返回
// 此处使用strlen获取字符串长度有很大的弊病,比如对字符串“新年快乐”要截取4个中文字符,
// 那么必须知道这4个中文字符的字节数,否则返回的字符串可能会是“新年快乐...”
if (strlen($string) <= $length) {
return $string;
}
// 转换原字符串中htmlspecialchars
$pre = chr(1);
$end = chr(1);
$string = str_replace ( array ('&','"','<','>' ),array ($pre . '&' . $end,$pre . '"' . $end,$pre . '<' . $end,$pre . '>' . $end ),$string );
$strcut = ''; // 初始化返回值
// 如果是utf-8编码(这个判断有点不全,有可能是utf8)
if (strtolower ( CHARSET ) == 'utf-8') {
// 初始连续循环指针$n,最后一个字位数$tn,截取的字符数$noc
$n = $tn = $noc = 0;
while ( $n < strlen ( $string ) ) {
$t = ord ( $string [$n] );
if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
// 如果是英语半角符号等,$n指针后移1位,$tn最后字是1位
$tn = 1;
$n++;
$noc++;
} elseif (194 <= $t && $t <= 223) {
// 如果是二字节字符$n指针后移2位,$tn最后字是2位
$tn = 2;
$n += 2;
$noc += 2;
} elseif (224 <= $t && $t <= 239) {
// 如果是三字节(可以理解为中字词),$n后移3位,$tn最后字是3位
$tn = 3;
$n += 3;
$noc += 2;
} elseif (240 <= $t && $t <= 247) {
$tn = 4;
$n += 4;
$noc += 2;
} elseif (248 <= $t && $t <= 251) {
$tn = 5;
$n += 5;
$noc += 2;
} elseif ($t == 252 || $t == 253) {
$tn = 6;
$n += 6;
$noc += 2;
} else {
$n++;
}
// 超过了要取的数就跳出连续循环
if ($noc >= $length) {
break;
}
}
// 这个地方是把最后一个字去掉,以备加$dot
if ($noc > $length) {
$n -= $tn;
}
$strcut = substr ( $string,$n );
} else {
// 并非utf-8编码的全角就后移2位
for ($i = 0; $i < $length; $i ++) {
$strcut .= ord ( $string [$i] ) > 127 ? $string [$i] . $string [++ $i] : $string [$i];
}
}
// 再还原最初的htmlspecialchars
$strcut = str_replace( array ($pre . '&' . $end,array ('&',$strcut );
$pos = strrpos ( $strcut,chr ( 1 ) );
if ($pos !== false) {
$strcut = substr ( $strcut,$pos );
}
return $strcut . $dot; // 最后把截取加上$dot输出
}
discuz版本的最大缺陷在于使用 strlen
获取原始字符串的长度,并用来和传入的要
截取长度参数(字节数)进行比较,由于UTF-8的
中文字符的字节数是不固定的,所以就会面临这样的窘境:如果要
截取4个
中文字符应该指定多大的
截取长度呢?8字节还是12字节呢?。。。这是无法预计的,也正是因为这个问题
discuz的cutstr实际是有bug的,通过下面的测试结果能看出:
<div class="codetitle">
<a style="CURSOR: pointer" data="66495" class="copybut" id="copybut66495" onclick="doCopy('code66495')"> 代码如下: <div class="codebody" id="code66495">
$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版
<div class="codetitle">
<a style="CURSOR: pointer" data="44469" class="copybut" id="copybut44469" onclick="doCopy('code44469')"> 代码如下: <div class="codebody" id="code44469">
/
[ecshop] 基于PHP的 mb_substr,iconv_substr 这两个扩展来截取字符串,中文字符都是按1个字符长度计算;
该
函数仅适用于utf-8编码的
中文字符串。
@p
aram $str 原始字符串
@param $length 截取的字符数
@p
aram $append 替换截掉部分的结尾字符串
@return 返回截取后的字符串
/
function sub_str($str,$length = 0,$append = '...') {
$str = trim($str);
$strlength = strlen($str);
if ($length == 0 || $length >= $strlength) {
return $str;
} elseif ($length < 0) {
$length = $strlength + $length;
if ($length < 0) {
$length = $strlength;
}
}
if ( function_exists('mb_substr') ) {
$newstr = mb_substr($str,'utf-8');
} elseif ( function_exists('iconv_substr') ) {
$newstr = iconv_substr($str,'utf-8');
} else {
//$newstr = trim_right(substr($str,$length));
$newstr = substr($str,$length);
}
if ($append && $str != $newstr) {
$newstr .= $append;
}
return $newstr;
}