放牧代码和思想
专注自然语言处理、机器学习算法
    愛しさ 優しさ すべて投げ出してもいい

汉字转拼音与简繁转换的Java实现

目录

最近HanLP希望支持拼音与繁体功能,所以学习了几个开源的Java实现,优化后集成进来。

开源项目地址:https://github.com/hankcs/HanLP 

/jpinyin

原理

这是GitHub上星星最多一个,主要原理就是利用一张HashTable将字与拼音一一对应起来。同时,在扫描的时候也会将当前汉字依次与后面的3个、2个、1个汉字组合,判断下是否存在多音字词组。也就是说,它最多支持4字词的多音字校正。同时,顺序扫描并且组合的话,复杂度的常数项有点高(大约是O(4n))。再乘上哈希表的复杂度,就是一个愚蠢、低效的实现。

词典格式

jpinyin中一共有3个表,分别是:

chinese.db 简繁词表

一共大约两千个汉字的简繁对应:

事实上,汉字的简繁对应并非严格的一对一,比如“皇后”的繁体应该是“皇后”,而“以后”的繁体应该是“以後”,两者并不相同。季先生着重谈到当年简化汉字时,也说把“皇后”的后与“以后”的“后”弄成一个字是遗憾。

所以这个词典就是个垃圾。

pinyin.db 汉字读音表

一共大约两万个汉字与它们的读音,支持多音字:

mutil_pinyin.db 多音词组

有些词语中的某个字读音与常用读音不同,比如鸭绿江。一共大约八百个:

这些词典都是以zip形式的Property储存的,而Property其实就是一个HashMap,所以这些词典可以视作哈希表。

算法

算法没什么可说的,基本步骤是:

  1. 先统统以字为单位转为简体

  2. 从前往后扫描,先尝试多音字识别处理(将当前汉字依次与后面的3个、2个、1个汉字组合,判断下是否存在多音字词组),如果没有查到多音词组,则以字为单位查询读音,取第一个(也就是说多音字并没有利用到)。

评价

个人评价极低,不支持简繁体分歧,在多音字的处理上没有用到更高效的算法,乏善可陈,渣渣。

/nlp-lang

这个项目是一个基本包,封装了大多数nlp项目中常用工具,其中就有简繁转换与拼音模块。

词典格式

fan2jian.dic 简繁体分歧词典

这是一个繁体到简体的词典,词汇量大约在五千左右,但是包含了一些汉字的简繁对照,所以真实词汇量会小很多:

pinyin.dic 拼音字词词典

这是一个汉字与词语到拼音的词典,词汇量在20万左右,同样包含汉字的拼音,所以真实词汇量会小很多:

算法

算法与词典在内存中的数据结构有很大关系,ansj这次使用了二分trie树来储存这些词典。

简繁转换算法

使用了二分trie树的前缀查询算法,比Hash表高效。关于二分trie树的更多讲解,请参考:《Trie树分词》。

值得注意的是,这里的繁转简词典是fan2jian.dic所示,简转繁词典则是fan2jian.dic的前后两个词串逆转过来合成的,这样做很聪明。当然,会损失一些词语,比如:

乙太網	        以太网
乙太網路	以太网

不过,ansj的词典还是弱了一点,把“皇后”转成了“皇後”。

拼音算法

拼音词典,储存采用了一个叫做SmartForest的结构。

/**
 * 一个小树,和Forest的区别是.这个在首字也是用二分查找,做过一次优化.达到到达一定量级自动扩展为hash定位 在ansj分词中这个应用是在自适应分词
 *
 * @author ansj
 */
public class SmartForest<T> implements Comparable<SmartForest<T>>

SmartForest依然是一棵trie树,只不过,当如果数组内元素接近于最大值直接数组定位(Forest则永远是二分定位):

添加:

            // 如果数组内元素接近于最大值直接数组定位,rate是内存和速度的一个平衡
            if (branches != null && branches.length >= MAX_SIZE * rate)
            {
                SmartForest<T>[] tempBranches = new SmartForest[MAX_SIZE];
                for (SmartForest<T> b : branches)
                {
                    tempBranches[b.getC()] = b;
                }
                tempBranches[branch.getC()] = branch;
                branches = null;
                branches = tempBranches;
            }
            else
            {
                SmartForest<T>[] newBranches = new SmartForest[branches.length + 1];
                int insert = -(bs + 1);
                System.arraycopy(this.branches, 0, newBranches, 0, insert);
                System.arraycopy(branches, insert, newBranches, insert + 1, branches.length - insert);
                newBranches[insert] = branch;
                this.branches = newBranches;
            }

查找:

    public int get(char c)
    {
        if (branches == null)
            return -1;
        if (branches.length == MAX_SIZE)
        {
            return c;
        }
        int i = Arrays.binarySearch(this.branches, new SmartForest<T>(c));
        return i;
    }

其他的并没有特别的,依然是Trie树分词》的那一套逻辑。

评价

算法给好评,词典给中评。

/chinese-utils

这是一套名不见经传的类库,作者在介绍中说“中文相关工具包,目前提供中文简繁体互转,以及中文转拼音。未来会提供中文分词。”,不清楚是否会履行诺言。

词典格式

pinyin.txt 汉字拼音字典

这是单个汉字与拼音的对照词典,大约有两万个常用与罕见的汉字:

polyphone.txt 多音词词典

与jpinyin类似,是异读词的集合,大约有一万词汇量:

非常全面,像这个“鱼丽于罶”还是第一次见到,我读书少,你们不要骗我

unknown.txt 未知读音的字的词典(这个名字好长我自己起的)

一些奇怪的汉字,可能是韩国或日本的汉字:

simp.txt trad.txt 简繁汉字对应词典

两个词典合起来就是简繁汉字对应词典了,作者把它们拆开了。

simplified.txt 繁简分歧词表

##### 繁简分歧词表 #####

# 计算机
印表機=打印机
記憶體=内存
乙太網=以太网
乙太網路=以太网
游標=光标
光碟=光盘
光碟機=光驱
軟碟機=软驱
匯流排=总线
碟片=盘片
硬體=硬件
硬碟=硬盘
磁碟=磁盘
磁軌=磁道
通信埠=端口
連接埠=端口
介面=接口
運算元=算子
演算法=算法

traditional.txt 简繁分歧词表

##### 简繁分歧词表 #####

# 计算机
打印机=印表機
内存=記憶體
以太网=乙太網
光标=游標
光盘=光碟
光驱=光碟機
软驱=軟碟機
总线=匯流排
盘片=碟片
硬件=硬體
硅谷=矽谷
硬盘=硬碟
磁盘=磁碟
磁道=磁軌
端口=通信埠
接口=介面
算子=運算元
算法=演算法
芯片=晶片

算法

作者实现了一棵基于哈希表的trie树,速度估计勉勉强强,内存估计够呛。

简繁转换

依然是《Trie树分词》的那一套逻辑,先从分歧词表查,查不到再从单字简繁对照表中查。

汉字转拼音

与简繁转换的算法相同,先从多音词词表中查,查不到的话从单字读音表中查。

评价

算法还行吧,词典特别棒,有“皇后”这种分歧词。算法给4颗星,词典给好评。

HanLP

正在集百家之长,准备用trie树和chinese-utils的词典做一个无限趋近完美的类库!

简繁转换

        HanLP.Config.enableDebug();
        System.out.println(HanLP.convertToSimplifiedChinese("「以後等妳當上皇后,就能買士多啤梨慶祝了」"));
        System.out.println(HanLP.convertToTraditionalChinese("“以后等你当上皇后,就能买草莓庆祝了”"));

输出

汉字转拼音

HanLP不仅支持基础的汉字转拼音,还支持声母、韵母、音调、音标和输入法首字母与首声母功能。HanLP能够识别多音字,也能给繁体中文注拼音。最重要的是,HanLP采用了双数组trie树和压缩的词典格式(现在HanLP已经升级到Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配,性能大幅提升!),能够提供毫秒级的响应速度!

        String text = "重载不是重担," + HanLP.convertToTraditionalChinese("以后爱皇后");
        List<Pinyin> pinyinList = PinyinDictionary.convertToPinyin(text);
        System.out.print("原文,");
        for (char c : text.toCharArray())
        {
            System.out.printf("%c,", c);
        }
        System.out.println();

        System.out.print("拼音(数字音调),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin);
        }
        System.out.println();

        System.out.print("拼音(符号音调),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getPinyinWithToneMark());
        }
        System.out.println();

        System.out.print("拼音(无音调),");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getPinyinWithoutTone());
        }
        System.out.println();

        System.out.print("声调,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getTone());
        }
        System.out.println();

        System.out.print("声母,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getShengmu());
        }
        System.out.println();

        System.out.print("韵母,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getYunmu());
        }
        System.out.println();

        System.out.print("输入法头,");
        for (Pinyin pinyin : pinyinList)
        {
            System.out.printf("%s,", pinyin.getHead());
        }
        System.out.println();

输出

原文
拼音(数字音调) chong2 zai3 bu4 shi4 zhong4 dan1 none5 yi3 hou4 ai4 huang2 hou4
拼音(符号音调) chóng zăi shì zhòng dān none hòu ài huáng hòu
拼音(无音调) chong zai bu shi zhong dan none yi hou ai huang hou
声调 2 3 4 4 4 1 5 3 4 4 2 4
声母 ch z b sh zh d none y h none h h
韵母 ong ai u i ong an none i ou ai uang ou
输入法头 ch z b sh zh d none y h a h h

如果上表有些字符显示异常,请看下图:

或者:

开源项目

本文代码已集成到HanLP中开源:http://www.hankcs.com/nlp/hanlp.html

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » 汉字转拼音与简繁转换的Java实现

我的作品

HanLP自然语言处理包《自然语言处理入门》