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

PHP 獲取漢字拼音首字母完整實現:支持中英混合、二分查找優化、多編碼兼容

     編輯:PHP 相關知識

功能特性

  • 純中文 → 拼音首字母大寫(如「測試字符串」→ CSZFC)
  • 純英文/數字 → 原樣返回(如 abc123 → abc123)
  • 中英混合 → 拼音首字母 + 英文/數字(如 我i我j → WIWJ)
  • 編碼自適應:輸入 UTF-8,內部轉 GB2312 查表
  • 二分查找優化:O(log n) 定位拼音區間,修復舊版 Z→Y 錯誤

核心類實現

<?php
/**
 * 漢字拼音首字母工具類
 * 支持 UTF-8 / GB2312 輸入
 * 算法:GB2312 區位碼映射 + 二分查找
 */
class Str2Pinyin
{
    /** GB2312 區位碼起始值 => 首字母映射表 */
    private $_pinyins = [
        176161 => 'A', 176197 => 'B', 178193 => 'C', 180238 => 'D',
        182234 => 'E', 183162 => 'F', 184193 => 'G', 185254 => 'H',
        187247 => 'J', 191166 => 'K', 192172 => 'L', 194232 => 'M',
        196195 => 'N', 197182 => 'O', 197190 => 'P', 198218 => 'Q',
        200187 => 'R', 200246 => 'S', 203250 => 'T', 205218 => 'W',
        206244 => 'X', 209185 => 'Y', 212209 => 'Z',
    ];

    private $_charset = 'utf-8';

    public function __construct(string $charset = 'utf-8')
    {
        $this->_charset = strtolower($charset);
    }

    /** 公開入口:獲取首字母串 */
    public function getInitials(string $str): string
    {
        if ($str === '') return '';
        // 純 ASCII 快速通道
        if ($this->_isAscii($str[0]) && $this->_isAsciis($str)) {
            return $str;
        }

        // 統一轉 GB2312 查表
        if ($this->_charset === 'utf-8') {
            $str = iconv('UTF-8', 'GB2312//IGNORE', $str);
        }

        $words = $this->_cutWord($str);
        $result = [];

        foreach ($words as $word) {
            if ($this->_isAscii($word)) {
                $result[] = $word; // 英文/數字/符號保留
                continue;
            }
            // 計算區位碼:高字節*1000 + 低字節
            $code = ord($word[0]) * 1000 + ord($word[1]);
            if (($idx = $this->_search($code)) !== -1) {
                $result[] = $this->_pinyins[$idx];
            }
        }
        return strtoupper(implode('', $result));
    }

    /* ========== 內部輔助方法 ========== */

    /** 判斷單字符是否 ASCII */
    private function _isAscii(string $char): bool
    {
        return ord($char[0]) < 160;
    }

    /** 判斷字符串前幾字符是否全 ASCII(避免誤判中英混合) */
    private function _isAsciis(string $str): bool
    {
        $len = min(3, strlen($str));
        for ($i = 0; $i < $len; $i++) {
            if (!$this->_isAscii($str[$i])) return false;
        }
        return true;
    }

    /** 按字符切分:中文雙字節、英文單字節 */
    private function _cutWord(string $str): array
    {
        $words = [];
        while ($str !== '') {
            if ($this->_isAscii($str)) {
                $words[] = $str[0];
                $str = substr($str, 1);
            } else {
                $word = $this->_msubstr($str, 0, 1); // 取一個漢字(雙字節)
                $words[] = $word;
                $str = substr($str, strlen($word));
            }
        }
        return $words;
    }

    /** 多字節安全 substr(GB2312 環境) */
    private function _msubstr(string $str, int $start, int $len): string
    {
        $start *= 2;
        $len *= 2;
        $result = '';
        $strlen = strlen($str);
        for ($i = 0; $i < $strlen; $i++) {
            if ($i >= $start && $i < $start + $len) {
                $result .= (ord($str[$i]) > 129) ? substr($str, $i++, 2) : $str[$i];
            } elseif (ord($str[$i]) > 129) {
                $i++; // 跳過雙字節第二字節
            }
        }
        return $result;
    }

    /** 二分查找區位碼對應的拼音索引 */
    private function _search(int $code): int
    {
        $keys = array_keys($this->_pinyins);
        $low = 0;
        $high = count($keys) - 1;

        if ($code < $keys[0]) return -1;

        while ($low <= $high) {
            $mid = (int) (($low + $high) / 2);
            if ($keys[$mid] < $code) {
                $low = $mid + 1;
            } elseif ($keys[$mid] > $code) {
                $high = $mid - 1;
            } else {
                return $keys[$mid]; // 精確命中
            }
        }
        // 未精確命中,返回小於 code 的最大鍵(區間查找)
        return ($high >= 0) ? $keys[$high] : -1;
    }
}

使用示例

<?php
$py = new Str2Pinyin();

// 純中文
echo $py->getInitials('測試字符串'); // CSZFC

// 純英文
echo $py->getInitials('abc123'); // abc123

// 中英混合
echo $py->getInitials('我i我j'); // WIWJ

// 長句子
echo $py->getInitials('啊吧才的餓飛就好i就看了嗎你哦平去人是他uv我想一在');
// ABCDEFGHIJKLMNOPQRSTUVWXYZ (演示全字母覆蓋)

// 指定 GB2312 輸入
$pyGbk = new Str2Pinyin('gb2312');
echo $pyGbk->getInitials('測試'); // CS

擴展建議

  1. 全拼支持:引入 overtrue/pinyin 等成熟庫,基於詞庫分詞獲取完整拼音。
  2. 多音字處理:首字母場景影響較小,全拼需結合上下文或詞頻消歧。
  3. 性能優化:靜態緩存映射表、編譯期生成 PHP 數組文件、避免重複 iconv 轉換。
  4. Unicode 擴展區:GB2312 覆蓋 6763 漢字,涉及生僻字需改用 Unicode 映射表(如 U+4E00-U+9FFF 區間)。

原理速覽:GB2312 區位碼與拼音分區

GB2312 將漢字按拼音排序編碼,每個拼音首字母佔據一個連續區間:

首字母區位碼範圍示例漢字
A1601-1636啊 阿 埃
B1637-1832巴 白 百
C1833-2077才 材 財
.........
Z7845-7940在 再 造

映射表鍵值 高字節*1000 + 低字節 即區位碼(如 1601 → 1601)。二分查找定位區間,即可得到首字母。

總結

這個類以 零依賴、極低內存、O(n log m) 時間複雜度 解決了首字母提取的核心需求。對於全拼、分詞、多音字等進階場景,建議併用 overtrue/pinyin、jiannei/laravel-pinyin 等成熟 Composer 包。理解區位碼原理,有助於在無擴展環境下快速實現定制化拼音功能。

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