必须显式传'UTF-8',不能省略第四个参数:mb_substr($str, 0, 10, 'UTF-8'),因省略时可能fallback到ISO-8859-1导致乱码,且PHP对编码名大小写和连字符敏感。

mb_substr() 必须显式传 'UTF-8',不能省略第四个参数
很多乱码问题不是函数本身有问题,而是漏写了编码参数:mb_substr($str, 0, 10) 在某些环境(比如旧版 PHP 或未配置 mbstring.internal_encoding)下会 fallback 到 ISO-8859-1,导致中文被当单字节切,结果是问号或方块。
正确写法永远带编码:mb_substr($str, 0, 10, 'UTF-8')。注意是 'UTF-8',不是 'utf8' 或 'utf-8'——PHP 对编码名大小写和连字符敏感,错一个字符都可能失效。
- Windows 下若报
Call to undefined function mb_substr(),说明php_mbstring.dll没启用,需检查php.ini中;extension=mbstring前的分号是否已去掉 -
mb_internal_encoding()只影响部分函数默认行为,不替代显式传参,别依赖它 - 若不确定源字符串编码,
mb_detect_encoding($str)不可靠,优先从源头统一为 UTF-8(数据库、HTTP header、文件保存格式)
截到标点或 emoji 末尾时怎么避免“半截符号”
直接 mb_substr($str, 0, $n, 'UTF-8') 可能停在句号前、顿号后、或者 emoji 中间(如 ? 被切成 )。这不是函数 bug,而是它按字符数硬截,不识别语义边界。
稳妥做法是:先截出初稿,再向后微调收尾位置:
立即学习“PHP免费学习笔记(深入)”;
- 用
mb_substr($str, 0, $n, 'UTF-8')得到候选字符串 - 检查末尾 1~2 字符是否属于常见断行不友好符号,例如:
。!?;:“”‘’()【】《》或空白符 - 若是,用
mb_substr()再向前收缩 1 或 2 位,直到末尾是汉字、英文字母或数字 - 更严格场景可用正则:
mb_ereg_replace('^(.{0,'.$n.'})(?:[^[:punct:][:space:]]|$).*', '\1', $str),但性能低,仅限管理后台等非高频路径
加省略号要手动拼接,别用 mb_strimwidth()
mb_strimwidth($str, 0, 20, '…', 'UTF-8') 看似方便,但它按“显示宽度”算:中文算 2 单位,英文算 1。同样设 20 宽度,纯中文串只留 10 字,纯英文却能塞 20 字——前端布局极易错乱。
真正可控的方式是两步走:
- 先用
mb_substr($str, 0, $n - 1, 'UTF-8')截取(预留 1 位给省略号) - 再手动拼接:
$result = $truncated . '…' - 注意:省略号
…是 1 个 Unicode 字符(U+2026),不是三个英文点,别写成'...' - 如果原始字符串本身就短于目标长度,就别加省略号,避免画蛇添足
性能敏感时怎么选:substr() 还是 mb_substr()?
实测 mb_substr() 比 substr() 慢 3–5 倍,因为要逐字节解析 UTF-8 编码。但慢不等于不能用——关键看输入是否可信。
- 若确定字段 100% 是 ASCII(如用户 ID、token、手机号、纯数字日志),直接用
substr(),安全且快 - 若含中文、emoji、用户昵称、评论内容,必须用
mb_substr(),别为了省几毫秒赌输入干净 - 极端情况(如批量处理万级评论摘要),可先用
mb_strlen($str, 'UTF-8') > $limit快速判断是否需要截取,避免无谓调用 - 别用
mb_strcut()替代mb_substr()——它按字节截,虽不乱码但会把“你好”截成“你”,因为“好”字在 UTF-8 中占 3 字节,容易切偏
最常被忽略的点:环境差异。本地开发开着 mbstring,上线服务器却没启扩展;测试数据全是英文,线上突然涌入大量 emoji —— 这些都不是函数问题,是部署和输入假设没对齐。



















