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

PHP 判斷字串字元類型:純數字、純英文、純漢字、混合型檢測完整指南

     編輯:PHP 相關知識

利用 strlen() (位元組長度) 與 mb_strlen() (字元長度) 的差異,可快速判斷字串組成:純 ASCII、純多位元組字元 (中日韓)、或混合型。本文提供完整檢測函數、原理解析、編碼陷阱與現代替代方案。

核心原理

  • strlen($str):返回字串位元組數
  • mb_strlen($str, 'UTF-8'):返回字串字元數 (Unicode 碼位)
  • ASCII 字元 (英文、數字、符號) 佔 1 位元組 → 兩者相等
  • UTF-8 中文佔 3 位元組 → strlen = 3 * mb_strlen
  • UTF-8 日文片假名/平假名佔 3 位元組、韓文佔 3 位元組、Emoji 佔 4 位元組

完整檢測函數

<?php
/**
 * 判斷字串字元組成類型
 * @param string $str 待檢測字串
 * @param string $encoding 字串編碼 (預設 UTF-8)
 * @return string 類型:'ascii' | 'cjk' | 'mixed' | 'empty' | 'other'
 *         ascii  - 純 ASCII (英文、數字、常用符號)
 *         cjk    - 純中日韓統一表意文字 (含漢字、假名、諺文)
 *         mixed  - ASCII 與 CJK 混合
 *         other  - 包含其他 Unicode 字元 (Emoji、特殊符號、拉丁擴展等)
 */
function detectStringType(string $str, string $encoding = 'UTF-8'): string {
    if ($str === '') return 'empty';

    $byteLen = strlen($str);
    $charLen = mb_strlen($str, $encoding);

    // 1. 純 ASCII:位元組數等於字元數
    if ($byteLen === $charLen) {
        // 進一步細分:純數字、純字母、字母數字混合
        if (ctype_digit($str)) return 'ascii_digit';
        if (ctype_alpha($str)) return 'ascii_alpha';
        if (ctype_alnum($str)) return 'ascii_alnum';
        return 'ascii'; // 含符號、空白等
    }

    // 2. 純多位元組:位元組數是字元數的整數倍 (通常 3 倍)
    // 注意:Emoji 為 4 位元組,會導致非整數倍
    $ratio = $byteLen / $charLen;
    if (abs($ratio - 3) < 0.01 || abs($ratio - 4) < 0.01) {
        // 粗略判斷為 CJK 區塊,更精確需用正則
        if (preg_match('/^[\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}]+$/u', $str)) {
            return 'cjk';
        }
        // 可能全為 Emoji 或其他 3/4 字節字元
        return 'other';
    }

    // 3. 混合型:位元組數不等於字元數,且非整數倍
    return 'mixed';
}

// 更精確的分類:使用 Unicode 正則
function classifyString(string $str): array {
    $hasAscii   = (bool)preg_match('/[\x00-\x7F]/', $str);
    $hasHan     = (bool)preg_match('/\p{Han}/u', $str);       // 漢字
    $hasKana    = (bool)preg_match('/\p{Hiragana}|\p{Katakana}/u', $str); // 日文假名
    $hasHangul  = (bool)preg_match('/\p{Hangul}/u', $str);    // 韓文諺文
    $hasEmoji   = (bool)preg_match('/\p{Extended_Pictographic}/u', $str); // Emoji
    $hasOther   = (bool)preg_match('/[^\x00-\x7F\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Extended_Pictographic}]/u', $str);

    $types = [];
    if ($hasAscii)   $types[] = 'ascii';
    if ($hasHan)     $types[] = 'han';
    if ($hasKana)    $types[] = 'kana';
    if ($hasHangul)  $types[] = 'hangul';
    if ($hasEmoji)   $types[] = 'emoji';
    if ($hasOther)   $types[] = 'other';

    return [
        'types' => $types,
        'desc'  => implode(' + ', $types) ?: 'empty',
    ];
}

// 測試
$tests = [
    '123456',
    'abcdef',
    'Hello World',
    '中文測試',
    'こんにちは',
    '한글',
    'Hello 世界',
    'PHP 8.2 🎉',
    '🎉🎊🎁',
    'αβγ', // 希臘字母
];

foreach ($tests as $t) {
    echo "{$t} => " . detectStringType($t) . " | " . classifyString($t)['desc'] . "\n";
}
/*
123456 => ascii_digit | ascii
abcdef => ascii_alpha | ascii
Hello World => ascii | ascii
中文測試 => cjk | han
こんにちは => cjk | kana
한글 => cjk | hangul
Hello 世界 => mixed | ascii + han
PHP 8.2 🎉 => mixed | ascii + emoji
🎉🎊🎁 => other | emoji
αβγ => other | other
*/
?>

常見應用場景

場景判斷邏輯代碼片段
用戶名驗證:僅允許中英文數字無 other、無 emoji、無特殊符號!$hasOther && !$hasEmoji && !$hasSpecial
密碼強度:必須含大小寫、數字、特殊符號檢測 ascii 子類型ctype_lower/cupper/digit + 符號正則
搜尋關鍵字:中文按字切分、英文按詞切分detectStringType 判斷後分流if($type==='cjk') jieba_cut(); else explode(' ')
資料清洗:移除 Emoji、控制字元preg_replace 移除 \p{Cc}、\p{Extended_Pictographic}preg_replace('/[\p{Cc}\p{Extended_Pictographic}]/u', '', $str)
截斷字串:按字元數而非位元組數mb_substr / mb_strimwidthmb_substr($str, 0, 50, 'UTF-8')

編碼陷阱與最佳實踐

  • 編碼不一致:mb_strlen($str, 'UTF-8') 必須與實際字串編碼一致。若字串為 GBK/Big5 卻用 UTF-8 計算,結果錯誤。統一專案編碼為 UTF-8。
  • BOM 頭:UTF-8 BOM (EF BB BF) 會被計入長度。讀取檔案時用 bom_remove() 或 mb_strpos 跳過。
  • 組合字元:如 é 可為單碼位 (U+00E9) 或組合 e + ´ (U+0301)。mb_strlen 計算碼位數,顯示寬度需用 grapheme_strlen() (intl)。
  • Emoji 序列:家庭 Emoji 👨‍👩‍👧‍👦 由多個碼位組成 (ZWJ 連接),mb_strlen 計為 7/11 碼位,實際顯示為 1 個字符。需用 grapheme_* 函數處理。
  • 性能:mb_strlen 較 strlen 慢。高頻調用時,可先 strlen 快速判斷:若相等則為純 ASCII,無需呼叫 mb_strlen。

現代替代:grapheme_* 函數 (intl 擴展)

<?php
// 真正的「使用者可見字元數」 (書寫字素簇)
$str = '👨‍👩‍👧‍👦'; // 家庭 Emoji
echo "mb_strlen: " . mb_strlen($str, 'UTF-8') . "\n"; // 11 (碼位數)
echo "grapheme_strlen: " . grapheme_strlen($str) . "\n"; // 1 (字素簇數)

// 截斷保持字素簇完整
echo grapheme_substr($str, 0, 1); // 👨‍👩‍👧‍👦
// mb_substr 會截斷成亂碼
?>

總結

判斷字串字元類型核心在於 strlen vs mb_strlen 差異與 Unicode 正則。建議:

  1. 統一專案編碼為 UTF-8,資料庫、連線、HTML、PHP 內部皆一致。
  2. 簡單場景用 detectStringType() 快速分類。
  3. 精確場景用 classifyString() 正則檢測各 Unicode 區塊。
  4. 涉及顯示寬度、截斷、計數時,優先使用 grapheme_* 函數 (需 intl 擴展)。
  5. 避免自行實作位元組運算,善用 PHP 內建多位元組字串函數與 Unicode 正則。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved