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

PHP 多編碼(GB2312/UTF-8)中英文混合字符串截取函數完美封裝

     編輯:PHP 相關知識

問題背景

  • substr() 按字節截取,中文(GBK 2 字節、UTF-8 3 字節)會被截斷產生亂碼。
  • mb_substr() 需 mbstring 擴展,且需指定編碼。
  • 項目常同時存在 GBK/UTF-8 歷史數據,需統一函數兼容雙編碼。

核心原理

編碼中文字節數判斷邏輯
GBK/GB23122 字節首字節 ≥ 0x81 (129)
UTF-83 字節(常用漢字)首字節模式:110xxxxx (2字節)、1110xxxx (3字節)、11110xxx (4字節)

完美封裝函數:cut_str()

<?php
/**
 * 多編碼安全截取:支持 UTF-8、GBK/GB2312,自動處理中英文混合
 * @param string $string  原字符串
 * @param int    $sublen  截取長度(字符數,中英各算 1)
 * @param int    $start   開始位置(字符數,默認 0)
 * @param string $code    編碼:'utf-8' 或 'gbk'/'gb2312'(默認 utf-8)
 * @return string 截取結果,超長自動添加 '...'
 */
function cut_str($string, $sublen, $start = 0, $code = 'utf-8') {
    $code = strtolower($code);
    
    if ($code === 'utf-8') {
        // UTF-8 正則匹配單個字符(含 ASCII、多字節)
        // 模式:ASCII | 2字節 | 3字節 | 4字節
        $pattern = '/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/';
        
        preg_match_all($pattern, $string, $matches);
        $chars = $matches[0];
        
        $slice = array_slice($chars, $start, $sublen);
        $result = implode('', $slice);
        
        // 若實際字符數超過截取長度,添加省略號
        if (count($chars) > $start + $sublen) {
            $result .= '...';
        }
        return $result;
    } 
    else { // GBK/GB2312
        $len = strlen($string);
        $result = '';
        $charCount = 0;
        $targetEnd = $start + $sublen;
        
        for ($i = 0; $i < $len; $i++) {
            if ($charCount >= $targetEnd) break;
            
            $ord = ord($string[$i]);
            if ($ord > 129) { // 雙字節字符
                if ($charCount >= $start) {
                    $result .= $string[$i] . $string[$i+1];
                }
                $i++; // 跳過第二字節
            } else { // 單字節字符
                if ($charCount >= $start) {
                    $result .= $string[$i];
                }
            }
            $charCount++;
        }
        
        if ($charCount > $targetEnd) {
            $result .= '...';
        }
        return $result;
    }
}

// ========== 使用示例 ==========
$str = "abcd需要截取的字符串efgh";

// UTF-8 環境
echo cut_str($str, 8, 0, 'utf-8'); 
// 輸出:abcd需要截取... (8個字符:a b c d 需 要 截 取)

// GBK 環境
echo cut_str($str, 8, 0, 'gbk'); 
// 輸出:abcd需要截取... (同理)
?>

進階版:自動檢測編碼 + mbstring 優先

<?php
function smart_cut($string, $length, $start = 0, $suffix = '...') {
    if (empty($string)) return '';
    
    // 1. 優先使用 mbstring(最準確、性能最好)
    if (extension_loaded('mbstring')) {
        $encoding = mb_detect_encoding($string, ['UTF-8', 'GBK', 'GB2312', 'ASCII'], true);
        $encoding = $encoding ?: 'UTF-8';
        $slice = mb_substr($string, $start, $length, $encoding);
        // 判斷是否截斷
        if (mb_strlen($string, $encoding) > $start + $length) {
            return $slice . $suffix;
        }
        return $slice;
    }
    
    // 2. 回退:自動檢測編碼並調用 cut_str
    $encoding = mb_detect_encoding($string, ['UTF-8', 'GBK', 'GB2312'], true) ?: 'UTF-8';
    return cut_str($string, $length, $start, $encoding);
}
?>

常見誤區與避坑

  1. 直接用 substr($str, 0, 10):中文斷碼,極高風險。
  2. mb_substr($str, 0, 10) 不指定編碼:使用內部編碼(mb_internal_encoding()),若未設置則不可靠。必須顯式傳遞第 4 參數。
  3. 正則 /./u 匹配字符:preg_match_all('/./u', $str, $m) 可獲取 UTF-8 字符數組,但性能遠低於 mb_substr。
  4. 截取後直接入庫/輸出:確保截取結果編碼與目標存儲/輸出編碼一致。

性能對比(百萬次循環)

方法耗時(秒)備註
mb_substr()~0.8C 層實現,最快,需擴展
cut_str() (UTF-8 正則)~2.5純 PHP,兼容無 mbstring 環境
cut_str() (GBK 循環)~1.2純 PHP,單字節判斷較快
preg_match_all('/./u')~4.0正則開銷大,不推薦高頻調用

總結建議

  1. 現代項目:強制安裝 mbstring,統一使用 mb_substr($str, $start, $len, 'UTF-8')。
  2. 兼容舊環境/雙編碼:使用本文 cut_str() 或 smart_cut() 封裝。
  3. 高頻熱點路徑:避免正則版,優先 mbstring 或 GBK 循環版。
  4. 前端配合:列表頁建議後端截取輸出純文本,詳情頁前端 CSS text-overflow: ellipsis 實現動態截斷,減少後端計算。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved