PHP substr() 按字節截取,多字節編碼 (UTF-8/GBK) 中文會導致亂碼。mb_substr() 支持指定編碼按字符截取,是處理中文截斷的標準方案。
mb_substr(string $string, int $start, int $length = null, string $encoding = mb_internal_encoding()) : string
<?php
$str = '我是誰'; // GBK 編碼文件
echo mb_substr($str, 0, 1, 'gbk'); // 輸出: 我
echo mb_substr($str, 1, 2, 'gbk'); // 輸出: 是誰
<?php
$str = '我abc是誰'; // UTF-8 編碼文件
echo mb_substr($str, 0, 2, 'utf-8'); // 輸出: 我a
echo mb_substr($str, 2, 3, 'utf-8'); // 輸出: bc是
中英混合無縫支持,按「字符」而非「字節」計算長度。
<?php
/**
* 多編碼安全截取
* @param string $str
* @param int $start
* @param int $length
* @param string $charset 自動檢測優先級: utf-8, gbk, gb2312, big5
* @param string $suffix 截斷後綴
* @return string
*/
function msubstr($str, $start = 0, $length, $charset = 'auto', $suffix = '...') {
if ($charset === 'auto') {
$charset = detectEncoding($str);
}
if (mb_strlen($str, $charset) <= $length) {
return $str;
}
return mb_substr($str, $start, $length, $charset) . $suffix;
}
/**
* 簡易編碼檢測
*/
function detectEncoding($str) {
$encodings = ['utf-8', 'gbk', 'gb2312', 'big5'];
foreach ($encodings as $enc) {
if (mb_check_encoding($str, $enc)) {
return $enc;
}
}
return 'utf-8'; // 默認
}
// 使用
echo msubstr('你好世界', 0, 2); // 你好...
echo msubstr('Hello世界', 0, 5); // Hello...
<?php
// 文件保存為 UTF-8,但代碼寫 'gbk'
$str = '中文'; // 實際是 UTF-8 字節
echo mb_substr($str, 0, 1, 'gbk'); // 亂碼或錯誤
// 解決:統一項目編碼為 UTF-8,代碼中統一用 'utf-8'
declare(encoding='UTF-8'); // PHP 5.3+ 聲明腳本編碼
# Ubuntu
sudo apt-get install php-mbstring
# CentOS
sudo yum install php-mbstring
# Windows php.ini 取消註釋
extension=mbstring
<?php
// MySQLi
$mysqli->set_charset('utf8mb4'); // 推薦 utf8mb4 支持 emoji
// PDO
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', $user, $pass);
// 確保讀出字符串編碼與 mb_substr 參數一致
<?php
$str = str_repeat('中文', 10000);
$iterations = 1000;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) substr($str, 0, 10);
echo "substr: " . (microtime(true) - $start) . "s\n";
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) mb_substr($str, 0, 10, 'utf-8');
echo "mb_substr: " . (microtime(true) - $start) . "s\n";
/* 輸出示例:
substr: 0.0012s
mb_substr: 0.0085s (約 7x 慢,但正確)
*/
正確性優先,性能差異在業務邏輯中通常可忽略。
<?php
// 按「用戶感知字符」(書寫字素簇) 截取,正確處理 emoji、組合字符
$str = '👨👩👧👦家庭'; // 1個 emoji 序列 + 2個漢字
echo mb_substr($str, 0, 1, 'utf-8'); // 可能截斷 emoji
echo grapheme_substr($str, 0, 1); // 完整截取 👨👩👧👦
涉及 emoji、複雜文字系統時,grapheme_* 系列函數更準確。