首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 解決 substr() 截取中文亂碼:mb_substr、mb_strimwidth、grapheme 完整方案與效能比較

     編輯:PHP 綜合知識

substr() 按位元組截取,中文 UTF-8 佔 3 位元組、Emoji 佔 4 位元組,直接用會切斷字元導致亂碼。本文系統整理 mb_substr()、mb_strimwidth()、grapheme_substr()、正則過濾四大方案,比較編碼支援、效能、功能差異,提供最佳實踐建議。

亂碼根因

$str = '中文測試';
echo substr($str, 0, 3); // '中' (切在 '文' 3 bytes 中間)
echo substr($str, 0, 6); // '中文' (剛好 2 個字 * 3 bytes)
  • UTF-8:中日韓字元 3 bytes,英文 1 byte,Emoji 4 bytes
  • substr() 完全不知編碼,純位元組操作

方案比較總表

函式擴展需求編碼支援單位功能效能推薦場景
mb_substr() mbstring (內建) UTF-8 等多種 字元 截取、可指定起始/長度/編碼 快 ✅ 通用首選
mb_strimwidth() mbstring UTF-8 等 寬度 (半形=1, 全形=2) 截取+自動加尾碼 (如 ...) 快 列表摘要、固定寬度顯示
grapheme_substr() intl (PECL) Unicode Grapheme Cluster 使用者感知字元 正確處理組合字元 (如 é = e + ´) 中等 Emoji、複雜文字、國際化嚴格場景
正則 preg_match_all('/./u') PCRE (內建) UTF-8 字元 字元 可迭代、過濾、轉陣列 慢 教學、特殊邏輯、無 mbstring 環境

方案一:mb_substr() 通用標準

<?php
$str = '中文測試abc';
echo mb_substr($str, 0, 4, 'UTF-8'); // '中文測'
echo mb_substr($str, 2, 3, 'UTF-8'); // '測試a'

// 封裝函式
function msubstr(string $str, int $start, int $length, string $encoding = 'UTF-8'): string {
    return mb_substr($str, $start, $length, $encoding);
}

方案二:mb_strimwidth() 固定寬度摘要 (含尾碼)

<?php
$str = '這是一段很長的中文標題需要截斷顯示';
echo mb_strimwidth($str, 0, 20, '...', 'UTF-8'); 
// '這是一段很長的中文...' (寬度 20: 中文算 2, 英文算 1)
echo mb_strimwidth($str, 0, 20, '', 'UTF-8'); // 無尾碼

方案三:grapheme_substr() 處理 Emoji/組合字元 (PHP 7.0+ intl)

<?php
$str = 'A👨‍👩‍👧‍👦B'; // 家庭 Emoji = 1 Grapheme Cluster (多碼點組合)
echo strlen($str); // 25 bytes
echo mb_strlen($str, 'UTF-8'); // 7 碼點
echo grapheme_strlen($str); // 3 Grapheme Clusters (A, 👨‍👩‍👧‍👦, B)

echo grapheme_substr($str, 0, 2); // 'A👨‍👩‍👧‍👦' 正確!
echo mb_substr($str, 0, 2, 'UTF-8'); // 'A👨' 切斷組合字元!

方案四:無 mbstring 環境的正則替代 (不推薦生產)

function utf8_substr(string $str, int $start, int $length): string {
    preg_match_all('/./u', $str, $matches);
    $chars = $matches[0];
    return implode('', array_slice($chars, $start, $length));
}

進階:截取並補全 HTML 標籤 (避免斷標籤)

function truncateHtml(string $html, int $maxLen, string $suffix = '...'): string {
    // 簡易版:移除標籤後截取,實際專案建議用 HTMLPurifier 或 DOMDocument
    $text = strip_tags($html);
    if (mb_strlen($text, 'UTF-8') <= $maxLen) return $html;
    return mb_strimwidth($text, 0, $maxLen, $suffix, 'UTF-8');
}

效能基準 (PHP 8.1, 100 萬字串截取 100 字元)

// mb_substr:        ~0.15 秒
// mb_strimwidth:    ~0.18 秒
// grapheme_substr:  ~0.45 秒 (intl 開銷)
// preg_match_all:   ~2.5 秒

最佳實踐建議

  1. 通用中文截取:mb_substr($str, $start, $len, 'UTF-8'),專案統一封裝 msubstr()。
  2. 列表摘要、卡片標題:mb_strimwidth($str, 0, $width, '...', 'UTF-8'),自動加尾碼、按視覺寬度。
  3. 含 Emoji/組合字元:安裝 intl 擴展,用 grapheme_substr(),唯一正確 方案。
  4. 資料庫層面:MySQL LEFT(col, n) 同樣按位元組,應用層截取更可控。
  5. 編碼統一:專案全域 mb_internal_encoding('UTF-8'),省略第 4 參數。

常見錯誤

錯誤後果修正
substr($str, 0, 10) 中文亂碼、切斷字元改用 mb_substr
mb_substr($str, 0, 10) 未指定編碼預設內部編碼可能非 UTF-8指定 'UTF-8' 或設定 mb_internal_encoding
Emoji 用 mb_substr切斷組合字元、顯示 用 grapheme_substr (需 intl)

相關專題:《PHP 字串用法總結》、《PHP 編碼與轉碼操作技巧匯總》。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved