利用国标一级汉字的起始区位码获取汉字对应拼音首字母

一直对于汉字转拼音的原理不是很清楚,这次正好有个需求要用到!于是就查查资料,利用国标一级汉字的起始区位码位置,再加上汉字读音首字母的对应组合就可以轻松的得到了汉字对应拼音的首字母。虽说是首字母,但是足够满足需求了。。。

首先来看看国标汉字起始区位码的基础知识

GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个。

分区表示:GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。
1)01-09区为特殊符号。
2)16-55区为一级汉字,按拼音排序。
3)56-87区为二级汉字,按部首/笔画排序。
4)10-15区及88-94区则未有编码。

举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。

最后附上国标一级汉字起始区位码的文件,看一下就很明了了。。。
汉字起始区位码

接着说说gb2312下汉字的字节结构

在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。

每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。

“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。

例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节) 0xA1(第二个字节)储存。区位码=区字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

再来看看实现的思路

  • 首先需要将使用的汉字转为gb2312的编码,因为起始区位码是按照GB2312标准的
  • 定义起始区位码和汉字读音首字母的对应的配置
  • 获取汉字的起始区位码 --汉字的两个字节的ASC码值减去160拼接的四位数字【第一个字节在前,不足两位的前面补0】
  • 根据得到的汉字起始区位码在之前的配置中按照相关算法匹配就可以得到

最后附上我按照自己思路实现的代码,想必也是最贴心的一步了,哈哈!

/**
 * 汉子转拼音首字母类【繁体字暂时不支持】
 * @author [yangbai] <[yangbai6644@163.com]>
 */
class ChineseToPinyinLetter {

    /**
     * gb2312编码下国标一级汉字的起始区位码返回:1601-8794
     * 国标一级汉字不同读音的起始区位码对应的拼音首字母
     * 注意:读音首字母没有I,U,V
     */
    protected static $cfg = array(
        1601 => 'A',
        1637 => 'B',
        1833 => 'C',
        2078 => 'D',
        2274 => 'E', 
        2302 => 'F', 
        2433 => 'G', 
        2594 => 'H', 
        2787 => 'J', 
        3106 => 'K', 
        3212 => 'L', 
        3472 => 'M',   
        3635 => 'N', 
        3722 => 'O', 
        3730 => 'P', 
        3858 => 'Q', 
        4027 => 'R', 
        4086 => 'S', 
        4390 => 'T', 
        4558 => 'W', 
        4684 => 'X', 
        4925 => 'Y', 
        5249 => 'Z', 
        5595 => '#',
        8795 => ''
    );
    /**
     * 获取国标一级汉字的起始区位码
     * @param  [type]
     * @return [type]
     */
    public static function getChineseStartRegionCode($str) {
        //首先将汉字从utf-8转成gb2312
        $str = mb_convert_encoding($str, 'gb2312', 'utf-8');
        //繁体字用这个函数转码会报错
        //$str = iconv('utf-8', 'gb2312', $str);
        //是中文则返回起始区位码,否则返回原字符的大写
        if (ord(substr($str, 0, 1)) > 0xa0) {
            return sprintf("%02d%02d", ord($str[0])-160, ord($str[1])-160);
        }
        return false;
    }

    /**
     * 获取汉字对应读音的首字母-大写
     * @param  [type]
     * @return [type]
     */
    public static function getChineseFirstLetter($str) {
        if (ord(substr($str, 0, 1)) <= 0xa0) {
            return strtoupper($str[0]);
        }
        $code = self::getChineseStartRegionCode($str);
        if ($code !== false) {
            $codes = array_keys(self::$cfg);
            $codes_count = count($codes);
            for ($i=0; $i<$codes_count; $i++) {
                if ($code>=$codes[$i] && $code<$codes[$i+1]) {
                    return self::$cfg[$codes[$i]];
                }
            }
        }
        return '非国标汉字!';
    }

    /**
     * 返回按照汉字拼音首字母分组的列表
     * @param  array
     * @return [type]
     */
    public static function getChineseListGroupByFirstLetter($str = array()) {
        $list = array();
        foreach ($str as $value) {
            $list[self::getChineseFirstLetter($value)][] = $value;
        }
        return $list;
    }

    /**
     * 返回按照汉字拼音首字母分组的列表组合
     * @param  array
     * @return [type]
     */
    public static function getChineseListGroupsByFirstLetter($str = array()) {
        $list = array();
        foreach ($str as $value) {
            if (isset($value['name'])) {
                $list[self::getChineseFirstLetter($value['name'])][] = $value;
            }
        }
        return $list;
    }
}

header("Content-Type:text/html;charset=utf-8");

echo ChineseToPinyinLetter::getChineseFirstLetter("杨");
echo ChineseToPinyinLetter::getChineseFirstLetter("齄");

echo "<pre>";
print_r(ChineseToPinyinLetter::getChineseFirstLetterGroup(['yangbai', '杨佰', '阳光', '刘惜君', '官恩娜', '唐嫣', '陈妍希', '胡围墙']));
友荐云推荐