在 PHP 程序开发中,经常会执行字符串的截取操作,比如输出信息列表时,标题不宜过长,打印文章摘要时,也要执行一系列的字符串截取操作。

遇到这些需求时,我们经常会想到使用 substr() 方法来实现,substr() 对全英文字符串的截取是比较适合的,但字符串只要出现中文字符,就有可能导致 PHP substr 中文乱码.

因为中文 UTF-8 编码,每个汉字占 3 字节,而 GB2312 占 2 字节,英文占 1 字节,截取位数不准确,substr() 硬生生地将一个中文字符“锯”成两半,造成断开的字符会把其后的.. 拉过来一起做一个字,所以出现了 PHP substr 中文乱码

这种截取字符的结果,肯定不是我们想要的结果,这种出现 PHP substr 中文乱码的情况,可能会导致程序无法正常运行。解决办法主要有两种:

一、使用 mbstring 扩展库的 mb_substr() 截取就不会出现乱码了。

可以用 mb_substr()/mb_strcut() 这个函数,mb_substr()/mb_strcut() 的用法与 substr() 相似,只是在 mb_substr()/mb_strcut 最后要加入多一个参数,以设定字符串的编码,但是一般的服务器都没打开 php_mbstring.dll,需要在 php.ini 在把 php_mbstring.dll 打开。

1
2
3
<?php
    echo mb_substr("php 中文字符 encode",0,4,"utf-8");
?>

如果未指定最后一个编码参数,会是三个字节为一个中文,这就是 utf-8 编码的特点,若加上 utf-8 字符集说明,所以,是以一个字为单位来截取的。

使用的时候要注意 php 文件的编码,和网页显示时的编码。使用这个 mb_substr 方法要事先知道字符串的编码,如果不知道编码,就需要判断,mbstring 库还提供了 mb_check_encoding 来检验字符串编码,但还不完善。

二、自己书写截取函数,但效率不如用 mbstring 扩展库来得高。下面是 ecshop 里面的截取 UTF-8 编码下字符串的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function sub_str($str$length = 0, $append = true)
{
    $str = trim($str);
    $strlength = strlen($str);

    if ($length == 0 || $length >= $strlength)
    {
        return $str;  // 截取长度等于 0 或大于等于本字符串的长度,返回字符串本身
    }
    elseif ($length < 0)  // 如果截取长度为负数
    {
        $length = $strlength + $length;// 那么截取长度就等于字符串长度减去截取长度
        if ($length < 0)
        {
            $length = $strlength;// 如果截取长度的绝对值大于字符串本身长度,则截取长度取字符串本身的长度
        }
    }

    if (function_exists('mb_substr'))
    {
        $newstr = mb_substr($str, 0, $length, EC_CHARSET);
    }
    elseif (function_exists('iconv_substr'))
    {
        $newstr = iconv_substr($str, 0, $length, EC_CHARSET);
    }
    else
    {
        //$newstr = trim_right(substr($str, 0, $length));
        $newstr = substr($str, 0, $length);
    }

    if ($append && $str != $newstr)
    {
        $newstr .= '...';
    }

    return $newstr;
}

本文地址 https://shaoshilei.com/2014-04/when-capturing-a-string-chinese-appear-to-solve-the-php-substr-garbled.html