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

PHP 中英文混排字串截取亂碼問題完整解決方案

     編輯:PHP 綜合知識

問題根源

PHP 內建 strlen()、substr() 按位元組操作。GB2312/Big5 中文字佔 2 位元組,UTF-8 中文字佔 3 位元組。直接按字元數截取極易將雙位元組字元切半,產生亂碼。

$str = "這是1個字符串"; // 含半形數字
if (strlen($str) > 10) $str = substr($str, 10) . "...";
// 第 10、11 位元組剛好切斷「符」字 → 亂碼

解決思路:按字元而非位元組計算

核心邏輯:遍歷字串,檢測當前位元組是否為多位元組字元首位元組(ord($char) > 127 或 > 0x80),若是則一次取 2(GBK)或 3(UTF-8)位元組,並調整迴圈索引。

GB2312/Big5 編碼適用函數

方案一:簡潔版 cutstr()

function cutstr($string, $length, $dot = '...') {
    $strcut = '';
    for ($i = 0; $i < $length - strlen($dot) - 1; $i++) {
        $strcut .= (ord($string[$i]) > 127)
            ? $string[$i] . $string[++$i]  // 雙位元組字元
            : $string[$i];                 // 單位元組字元
    }
    return $strcut . $dot;
}

方案二:嚴謹版 cutTitle()(含長度校驗)

function cutTitle($str, $len, $tail = "") {
    $length = strlen($str);
    $lentail = strlen($tail);
    $result = "";
    if ($length > $len) {
        $len -= $lentail;
        for ($i = 0; $i < $len; $i++) {
            if (ord($str[$i]) < 127) {
                $result .= $str[$i];
            } else {
                $result .= $str[$i] . $str[++$i];
            }
        }
        $result = (strlen($result) > $len)
            ? substr($result, 0, -2) . $tail
            : $result . $tail;
    } else {
        $result = $str;
    }
    return $result;
}

方案三:通用 mysubstr($str, $start, $len)

function mysubstr($str, $start, $len) {
    $tmpstr = "";
    $strlen = $start + $len;
    for ($i = 0; $i < $strlen; $i++) {
        if (ord(substr($str, $i, 1)) > 0xA0) { // GBK 範圍
            $tmpstr .= substr($str, $i, 2);
            $i++;
        } else {
            $tmpstr .= substr($str, $i, 1);
        }
    }
    return $tmpstr;
}

UTF-8 編碼適用函數

方案一:正則表達式 utf8Substr()

function utf8Substr($str, $from, $len) {
    return preg_replace(
        '#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$from.'}'.
        '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$len.'}).*#s',
        '$1', $str);
}

利用 UTF-8 位元組特徵(首位元組 0xC0-0xFF,續位元組 0x80-0xBF)精準匹配字元。

方案二:雙編碼自動偵測 cut_str()

function cut_str($string, $sublen, $start = 0, $code = 'UTF-8') {
    if ($code == 'UTF-8') {
        $pa = "/[\x01-\x7F]|[\xC0-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/";
        preg_match_all($pa, $string, $t_string);
        $slice = array_slice($t_string[0], $start, $sublen);
        return (count($t_string[0]) - $start > $sublen)
            ? join('', $slice) . "..."
            : join('', $slice);
    } else { // GB2312
        $start *= 2; $sublen *= 2;
        $strlen = strlen($string);
        $tmpstr = '';
        for ($i = 0; $i < $strlen; $i++) {
            if ($i >= $start && $i < $start + $sublen) {
                $tmpstr .= (ord(substr($string, $i, 1)) > 129)
                    ? substr($string, $i, 2) : substr($string, $i, 1);
            }
            if (ord(substr($string, $i, 1)) > 129) $i++;
        }
        return (strlen($tmpstr) < $strlen) ? $tmpstr . "..." : $tmpstr;
    }
}

方案三:BugFree 專案實戰 sysSubStr()

function sysSubStr($String, $Length, $Append = false) {
    if (strlen($String) <= $Length) return $String;
    $I = 0; $StringLast = [];
    while ($I < $Length) {
        $StringTMP = substr($String, $I, 1);
        $ord = ord($StringTMP);
        if ($ord >= 224) { $StringTMP = substr($String, $I, 3); $I += 3; } // UTF-8 3 bytes
        elseif ($ord >= 192) { $StringTMP = substr($String, $I, 2); $I += 2; } // UTF-8 2 bytes / GBK
        else { $I++; }
        $StringLast[] = $StringTMP;
    }
    $result = implode("", $StringLast);
    return $Append ? $result . "..." : $result;
}

現代 PHP 最佳實踐(PHP 5.6+)

強烈建議使用 mbstring 擴展提供的多位元組安全函數:

mb_internal_encoding("UTF-8"); // 或 GBK
$sub = mb_substr($str, 0, 10, "UTF-8");
$len = mb_strlen($str, "UTF-8");
  • 自動處理編碼、字元寬度、起始位置。
  • 效能優於手寫正則,碼簡潔可維護。
  • 專案統一採用 UTF-8,資料庫、HTML、PHP 檔案編碼三位一體。

總結建議

  1. 新專案全站 UTF-8,直接用 mb_substr/mb_strlen。
  2. 維護舊專案(GBK)可採用 cut_str 雙模式函數。
  3. 資料庫連線執行 SET NAMES utf8(或 gbk),確保存取編碼一致。
  4. 輸出頁面標註 <meta charset="UTF-8">。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved