PHP截取UTF8字符串 utf-8 可以能占一个字符 二个字符 或者三个字符

时间:2023-01-10 18:01:04

PHP截取UTF8字符串  


想必很多人从一开始接触编程到现在,都有一个惯性思维:英文字符占用一个字节,中文字符占用两个字节。不错,英文字符是占用一个字节,但中文字符占用两个字节是相对于GBK编码而言(当然,其他一些编码如GB2312也是),但是在时下国际流行的UTF8编码中,一个中文字符占用3个字节。不要惊讶,这是一个事实,而且应该成为一个常识。
UTF8编码可能出现一个字符占用1个、2个、3个甚至更多字节的情况,如英文字符abc占用一个字节,中文字符占用三个字节,那么什么字符占用两个字节呢?这个问题我一开始并没有发现,只是前几天有人留言,首页的评论截取竟然出现了乱码的情况:

PHP截取UTF8字符串 utf-8 可以能占一个字符 二个字符 或者三个字符
最开始并没有发现这两个乱码出现的问题在哪里,后来仔细验证了下,发现是处在·这个字符上(键盘左上角,中文输入法下),它占用两个字节。而emlog的截取字符串函数,除了英文字符外,默认其他的都占三个字节了,因此导致乱码出现。
查阅了相关资料,得出了一个结论:UTF8编码的字符中,第一个字节ASCII值大于等于224的,其与之后的2个字节一起组成一个UTF8字符,第一个字节ASCII值大于192等于小于224的,其与之后的1个字节组成一个UTF-8字符,第一个字节ASCII值小于192的,其本身成为一个UTF8字符。于是在PHP中将·字符的ASCII打印出来,第一个字节是194,第二个字节是183,木有第三个字节了,于是截取的字符中,若包含·字符,就会出现乱码了。
问题找到,解决方案也就很简单了,分别判断处理下就OK。写了如下函数用于截取:


function subString($str, $start, $length) {
    $i = 0;
    //完整排除之前的UTF8字符
    while($i < $start) {
        $ord = ord($str{$i});
        if($ord < 192) {
            $i++;
        } elseif($ord <224) {
            $i += 2;
        } else {
            $i += 3;
        }
    }
    //开始截取
    $result = '';
    while($i < $start + $length && $i < strlen($str)) {
        $ord = ord($str{$i});
        if($ord < 192) {
            $result .= $str{$i};
            $i++;
        } elseif($ord <224) {
            $result .= $str{$i}.$str{$i+1};
            $i += 2;
        } else {
            $result .= $str{$i}.$str{$i+1}.$str{$i+2};
            $i += 3;
        }
    }
    if($i < strlen($str)) {
        $result .= '...';
    }
    return $result;
}