在PHP中,substr()函数截取带有中文字符串的话,可能会出现乱码,这是因为中西文一个字节所占有的字节数不一样,而substr()的长度参数是按照字节去算的。substr()截取位数不准确,substr()硬生生地将一个中文字符“锯”成两半,造成断开的字符会把其后的码位拉过来一起做一个字,所以出现了PHP substr()截取中文乱码现象。
在GB2312编码时,一个中文占2个字节,英文为1个字节,而在UTF-8编码当中,一个中文可能占有2个或3个字节,英文或半角标点占1字节。
0. 原生解决方案
PHP中有原生的解决方案,mb_substr()方法。需要开启mbstring扩展,方法:
在php的配置文件中,寻找到extension=php_mbstring.dll,确保扩展被加载——此行首无分号。

下面重点讲使用基本函数substr去实现中文字符串的截取。
1. 基于substr()解决方案
1.1解决思路:
UTF-8编码的字符可能由1-3个字节组成,具体数目可以由第一个字节判断出来。
第一个字节大于224的,它与它之后的2个字节一起组成一个UTF-8字符
第一个字节大于192小于224的,它与它之后的1个字节组成一个UTF-8字符,否则第一个字节本身就是一个英文字符(包括数字和一小部分标点符号)。
1.2 substr()语法
substr()中文文档
string substr ( string $string , int $start [, int $length ] )
参数
string
输入字符串。必须至少有一个字符。
start
如果 start 是非负数,返回的字符串将从 string 的 start 位置开始,从 0 开始计算。
如果 start 是负数,返回的字符串将从 string 结尾处向前数第 start 个字符开始。
如果 string 的长度小于 start ,将返回 FALSE 。
length
如果提供了正数的 length,返回的字符串将从 start 处开始最多包括 length 个字符(取决于 string 的长度)。
如果提供了负数的 length ,那么 string 末尾处的 length 个字符将会被省略(若 start 是负数则从字符串尾部算起)。
如果 start 不在这段文本中,那么将返回 FALSE 。
如果提供了值为 0 ,FALSE 或 NULL 的 length,那么将返回一个空字符串。
如果没有提供 length,返回的子字符串将从 start 位置开始直到字符串结尾。
返回值
返回提取的子字符串, 或者在失败时返回 **FALSE
**。
1.3 myGBsubstr()
 function myGBsubstr($string, $start, $length) {
    if (strlen($string) > $length) {
        $str = null;
        $len = 0;
        $i = $start;
        while ( $len < $length) {
        if (ord(substr($string, $i, 1)) > 0xc0) {
            $str .=substr($string, $i, 3);
            $i+= 3;
        }elseif (ord(substr($string, $i, 1)) > 0xa0) {
            $str .= substr($string, $i, 2);
            $i+= 2;
        }else {
            $str.=substr($string, $i, 1);
            $i++;
        }
        $len ++;
        }
        return $str;
    }else {
        return $string;
    }
}
1.4 优化
判断mbstring模块是否可用,如果可用使用原生的mb_substr()函数,反之,使用自己定义的中文字符串截取函数。
function GBsubstr($string, $start,$length) {
    if (!function_exists(mb_substr)) {
        myGBsubstr($string, $start, $length);
    } else {
        mb_substr($string, $start, $length, 'utf8');
    }
}