没想到自己新年的第一篇博文竟然拖了这么久,在这里先给大家拜个晚年了,祝愿大家在新的一年里能一帆风顺,万事如意。

前面有发表一篇博文《overflow 的使用,用 css 截取标题》介绍了使用 css 中 overflow 来截取字符串,但是有些环境下需要使用 PHP 来截取一定长度的字符串,下面介绍使用 PHP 来截取字符串不出现乱码的方法。

字符编码

简体中文的字符编码一般情况就是 GB2312 和 utf-8 这两种比较常用,GB2312 中的字符可能占有一个或两个字节,其中一个字节的字符最高位为 "0",剩余 7 位为编码位,对应的是 ASCII 码,两个字节的字符第一个字节最高位为 "1",剩余的 15 位为编码位,使用一个语句就可以解决 "$strcut .= ord($string[$i]) > 127 ? $string[$i].$string[++$i] : $string[$i];"而 utf-8 的编码方式就有些复杂了,字符可能占 1-6 个字节。需要根据第一个字节的大小分别判断。

字符大小

字符的大小分为字符所占像素的大小和字符所占字节的大小. 相同的字符不同编码所占字节大小不同,正如前面所写 utf-8 编码的字符可能占有 1-6 个字节; 字符在同种字体大小下高度基本相同,而宽度分为全角和半角字符,全角字符为半角字符的二倍,英文字符和数字一般为半角字符,而中文为全角字符。所以中文宽度为英文字符宽度的二倍。截取字符串要保证在像素宽度上基本相等,而不是字节数的相等。

在截取字符中还应该注意&(&), "("), <(<), >(>)等字符的截取,保证这些字符不能断开。

下面就是从 Discuz! 的文件中提取修改的 cutstr() 函数,基本可以算是一个比较完美的函数了。
function cutstr($string, $length, $charset, $dot = '...') {
    if(strlen($string) <= $length) {
        return $string;
    }

    $pre = chr(1);
    $end = chr(1);
    $string = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'), array($pre.'&'.$end, $pre.'"'.$end, $pre.'<'.$end, $pre.'>'.$end), $string);

    $strcut = '';
    if(strtolower($charset) == 'utf-8') {

        $n = $tn = $noc = 0;
        while($n < strlen($string)) {

            $t = ord($string[$n]);
            if($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
                $tn = 1; $n++; $noc++;
            } elseif(194 <= $t && $t <= 223) {
                $tn = 2; $n += 2; $noc += 2;
            } elseif(224 <= $t && $t <= 239) {
                $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;
            }

        }
        if($noc > $length) {
            $n -= $tn;
        }

        $strcut = substr($string, 0, $n);

    } else {
        for($i = 0; $i < $length; $i++) {
            $strcut .= ord($string[$i]) > 127 ? $string[$i].$string[++$i] : $string[$i];
        }
    }

    $strcut = str_replace(array($pre.'&'.$end, $pre.'"'.$end, $pre.'<'.$end, $pre.'>'.$end), array('&amp;', '&quot;', '&lt;', '&gt;'), $strcut);

    $pos = strrpos($strcut, chr(1));
    if($pos !== false) {
        $strcut = substr($strcut,0,$pos);
    }
    return $strcut.$dot;
}