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

PHP 正確獲取 UTF-8 字符串長度(兼容中英文混排)

     編輯:PHP 相關知識

PHP 內置 strlen() 返回的是字符串的字節數,而非字符數。對於 UTF-8 編碼,一個漢字佔 3 字節,英文字母佔 1 字節,直接使用 strlen() 會導致長度計算錯誤,影響表單驗證、截取、對齊等邏輯。

問題演示

<?php
$str1 = 'Hello world!中';
echo strlen($str1); // 輸出 14 (12字母+1空格+1感嘆號+3字節中文) - 實際字符數 13

$str2 = '你好,世界!';
echo strlen($str2); // UTF-8 下輸出 18 (6漢字×3) - 實際字符數 6
?>

標準解決方案:mb_strlen()(推薦)

PHP mbstring 擴展提供多字節字符串函數,mb_strlen() 可正確返回字符數。

<?php
$str = '你好,世界!';
echo mb_strlen($str, 'UTF-8'); // 輸出 6
echo mb_strlen($str);          // 使用內部編碼(需 php.ini 設置 mbstring.internal_encoding)
?>
  • 需確保服務器安裝並啟用 mbstring 擴展(現代 PHP 環境默認開啟)。
  • 顯式指定編碼 'UTF-8' 最安全,避免依賴全局配置。

無 mbstring 環境下的替代方案

方案一:正則匹配 Unicode 字符(WordPress 採用)

<?php
function utf8_strlen($string = null) {
    if ($string === null) return 0;
    // /u 修飾符按 Unicode 字符匹配,/s 讓 . 匹配換行
    preg_match_all('/./us', $string, $matches);
    return count($matches[0]);
}

echo utf8_strlen('你好,世界!'); // 6
echo utf8_strlen('Hello 世界');   // 8
?>

原理: preg_match_all('/./us', ...) 將字符串分解為單個 Unicode 碼點,統計匹配數即為字符數。

方案二:兼容 GBK/UTF-8 混合輸入的健壯函數

<?php
function mb_strlen_compat($str) {
    // 嘗試將 GBK 轉為 UTF-8(若已是 UTF-8 則 iconv 返回 false 或原串)
    $utf8 = @iconv('GBK', 'UTF-8//IGNORE', $str);
    if ($utf8 !== false && $utf8 !== '') {
        $str = $utf8;
    }
    // 統計 Unicode 字符
    preg_match_all('/./us', $str, $matches);
    return count($matches[0]);
}

// 測試
$gbk_str = iconv('UTF-8', 'GBK', '測試字符串');
echo mb_strlen_compat($gbk_str); // 正確輸出 5
echo mb_strlen_compat('測試字符串'); // 正確輸出 5
?>

常見場景對比

函數返回值含義UTF-8 中文適用場景
strlen()字節數1 個漢字 = 3二進制數據、文件大小、原始字節操作
mb_strlen()字符數1 個漢字 = 1標準首選,表單驗證、截取、顯示長度
preg_match_all('/./us')字符數1 個漢字 = 1無 mbstring 環境、兼容性代碼
iconv_strlen()字符數1 個漢字 = 1需 iconv 擴展,功能同 mb_strlen

最佳實踐建議

  1. 項目統一使用 mb_strlen(),在框架基類或工具類中封裝 str_len($str) 統一入口。
  2. php.ini 設置: mbstring.internal_encoding = UTF-8,mbstring.func_overload = 0(不覆蓋原生函數)。
  3. 截取字符串: 配合 mb_substr()、mb_strcut() 使用,切勿用 substr()。
  4. 數據庫字段長度: MySQL VARCHAR(255) 指字符數(utf8mb4),不受字節影響,但 PHP 端驗證必須用字符數。
  5. 前端同步: JavaScript str.length 返回 UTF-16 代碼單元數(Emoji 算 2),PHP 端用 mb_strlen(),雙端驗證邏輯需對齊。

完整工具函數封裝

<?php
/**
 * 獲取字符串字符長度(兼容 UTF-8/GBK,優先使用 mbstring)
 * @param string $str
 * @return int
 */
function str_len($str) {
    if (!is_string($str)) $str = (string)$str;
    if (function_exists('mb_strlen')) {
        return mb_strlen($str, 'UTF-8');
    }
    // 降級方案
    $utf8 = @iconv('GBK', 'UTF-8//IGNORE', $str);
    if ($utf8 !== false && $utf8 !== '') $str = $utf8;
    preg_match_all('/./us', $str, $m);
    return count($m[0]);
}

/**
 * 安全截取字符串
 * @param string $str
 * @param int $start
 * @param int|null $length
 * @return string
 */
function str_sub($str, $start, $length = null) {
    if (function_exists('mb_substr')) {
        return mb_substr($str, $start, $length, 'UTF-8');
    }
    // 降級:正則分解後切片
    preg_match_all('/./us', $str, $m);
    $chars = $m[0];
    return implode('', array_slice($chars, $start, $length));
}
?>

正確處理多字節字符串長度是 PHP 國際化開發的基礎功課。優先使用 mbstring 函數族,無擴展環境下用正則降級,封裝統一工具函數,從源頭杜絕長度計算錯誤導致的業務 Bug。

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