发布:2021/12/11 13:23:44作者:管理员 来源:本站 浏览次数:1072
网上关于JS实现汉字和拼音互转的文章很多,但是比较杂乱,都是互相抄来抄去,而且有的不支持多音字,有的不支持声调,有的字典文件太大,还比如有时候我仅仅是需要获取汉字拼音首字母却要引入200kb的字典文件,无法根据实际需要满足需求。
综上,我精心整理并修改了网上几种常见的字典文件并简单封装了一下可以直接拿来用的工具库。
鉴于很多人都比较关心多音字的问题,所以单独拿出一个小章节来介绍多音字的相关问题。
准确识别各种复杂语句中混杂的多音字其实并没有那么容易,有两个关键的地方,一个是多音字词库的丰富程度,一个是能否正确的给语句进行分词。而词库和分词的实现都需要一个非常丰富的词典文件,现代汉语词语有多少个,估计没有人算得清,再加上每天新出现的人名、网络词语、科技词语等等。一个普通的词库文件至少也有几百kb,所以不太适合web环境下去实现,一般最好放在服务器端做成一个接口。
鉴于很多人都希望有多音字识别的功能,所以我简单实现了一个版本。词库文件是从这里找到的,并根据实际情况将文件从1.8M压缩到了912kb,分词暂时只是自己非常简单的实现(也可以说压根就没有分词),如果是服务器端推荐几个不错的中文分词工具:Python版的Jieba和NodeJieba,性能非常好,其它语言版的参考上面项目的README。
关于分词,摘抄一段网络解释:
词是最小的能够独立活动的有意义的语言成分,英文单词之间是以空格作为自然分界符的,而汉语是以字为基本的书写单位,词语之间没有明显的区分标记,因此,要对中文信息进行处理,正确的分词就显得尤为关键。
比如看中国这一个词,单独的看中读kàn zhòng,中国读zhōng guó,连在一起却读作kàn zhōng guó。
我这个实现非常得简单,效果一般,性能也一般,需要下载将近1M的词库文件,所以不适合web环境
汉字转拼音:
输出类型:
多音字:
转换结果:
nín hǎo , zhōng guó dī wēn
【核心代码】
/**
* 汉字与拼音互转工具,根据导入的字典文件的不同支持不同
* 对于多音字目前只是将所有可能的组合输出,准确识别多音字需要完善的词库,而词库文件往往比字库还要大,所以不太适合web环境。
* @start 2016-09-26
* @last 2016-09-29
*/
;(function()
{
var dict = {}; // 存储所有字典数据
var pinyinUtil =
{
/**
* 解析各种字典文件,所需的字典文件必须在本JS之前导入
*/
parseDict: function()
{
// 如果导入了 pinyin_dict_firstletter.js
if(window.pinyin_dict_firstletter)
{
dict.firstletter = pinyin_dict_firstletter;
}
// 如果导入了 pinyin_dict_notone.js
if(window.pinyin_dict_notone)
{
dict.notone = {};
dict.py2hz = pinyin_dict_notone; // 拼音转汉字
for(var i in pinyin_dict_notone)
{
var temp = pinyin_dict_notone[i];
for(var j=0, len=temp.length; j<len; j )
{
dict.notone[temp[j]] = i; // 不考虑多音字
}
}
}
// 如果导入了 pinyin_dict_withtone.js
if(window.pinyin_dict_withtone)
{
dict.withtone = {};
var temp = pinyin_dict_withtone.split(',');
for(var i=0, len = temp.length; i<len; i )
{
// 这段代码耗时28毫秒左右,对性能影响不大,所以一次性处理完毕
dict.withtone[String.fromCharCode(i 19968)] = temp[i]; // 这里先不进行split(' '),因为一次性循环2万次split比较消耗性能
}
// 拼音 -> 汉字
if(window.pinyin_dict_notone)
{
// 对于拼音转汉字,我们优先使用pinyin_dict_notone字典文件
// 因为这个字典文件不包含生僻字,且已按照汉字使用频率排序
dict.py2hz = pinyin_dict_notone; // 拼音转汉字
}
else
{
// 将字典文件解析成拼音->汉字的结构
// 与先分割后逐个去掉声调相比,先一次性全部去掉声调然后再分割速度至少快了3倍,前者大约需要120毫秒,后者大约只需要30毫秒(Chrome下)
var notone = pinyinUtil.removeTone(pinyin_dict_withtone).split(',');
var py2hz = {}, py, hz;
for(var i=0, len = notone.length; i<len; i )
{
hz = String.fromCharCode(i 19968); // 汉字
// = aaa[i];
py = notone[i].split(' '); // 去掉了声调的拼音数组
for(var j=0; j<py.length; j )
{
py2hz[py[j]] = (py2hz[py[j]] || '') hz;
}
}
dict.py2hz = py2hz;
}
}
},
/**
* 根据汉字获取拼音,如果不是汉字直接返回原字符
* @param chinese 要转换的汉字
* @param splitter 分隔字符,默认用空格分隔
* @param withtone 返回结果是否包含声调,默认是
* @param polyphone 是否支持多音字,默认否
*/
getPinyin: function(chinese, splitter, withtone, polyphone)
{
if(!chinese || /^ $/g.test(chinese)) return '';
splitter = splitter == undefined ? ' ' : splitter;
withtone = withtone == undefined ? true : withtone;
polyphone = polyphone == undefined ? false : polyphone;
var result = [];
if(dict.withtone) // 优先使用带声调的字典文件
{
for (var i=0, len = chinese.length; i < len; i )
{
var pinyin = dict.withtone[chinese[i]];
if(pinyin)
{
if(!polyphone) pinyin = pinyin.replace(/ .*$/g, ''); // 如果不需要多音字
if(!withtone) pinyin = this.removeTone(pinyin); // 如果不需要声调
}
result.push(pinyin || chinese[i]);
}
}
else if(dict.notone) // 使用没有声调的字典文件
{
if(withtone) console.warn('pinyin_dict_notone 字典文件不支持声调!');
if(polyphone) console.warn('pinyin_dict_notone 字典文件不支持多音字!');
for (var i=0, len = chinese.length; i < len; i )
{
var temp = chinese.charAt(i);
result.push(dict.notone[temp] || temp);
}
}
else
{
throw '抱歉,未找到合适的拼音字典文件!';
}
if(!polyphone) return result.join(splitter);
else
{
if(window.pinyin_dict_polyphone) return parsePolyphone(chinese, result, splitter, withtone);
else return handlePolyphone(result, ' ', splitter);
}
},
/**
* 获取汉字的拼音首字母
* @param str 汉字字符串,如果遇到非汉字则原样返回
* @param polyphone 是否支持多音字,默认false,如果为true,会返回所有可能的组合数组
*/
getFirstLetter: function(str, polyphone)
{
polyphone = polyphone == undefined ? false : polyphone;
if(!str || /^ $/g.test(str)) return '';
if(dict.firstletter) // 使用首字母字典文件
{
var result = [];
for(var i=0; i<str.length; i )
{
var unicode = str.charCodeAt(i);
var ch = str.charAt(i);
if(unicode >= 19968 && unicode <= 40869)
{
ch = dict.firstletter.all.charAt(unicode-19968);
if(polyphone) ch = dict.firstletter.polyphone[unicode] || ch;
}
result.push(ch);
}
if(!polyphone) return result.join(''); // 如果不用管多音字,直接将数组拼接成字符串
else return handlePolyphone(result, '', ''); // 处理多音字,此时的result类似于:['D', 'ZC', 'F']
}
else
{
var py = this.getPinyin(str, ' ', false, polyphone);
py = py instanceof Array ? py : [py];
var result = [];
for(var i=0; i<py.length; i )
{
result.push(py[i].replace(/(^| )(\w)\w*/g, function(m,$1,$2){return $2.toUpperCase();}));
}
if(!polyphone) return result[0];
else return simpleUnique(result);
}
},
/**
* 拼音转汉字,只支持单个汉字,返回所有匹配的汉字组合
* @param pinyin 单个汉字的拼音,不能包含声调
*/
getHanzi: function(pinyin)
{
if(!dict.py2hz)
{
throw '抱歉,未找到合适的拼音字典文件!';
}
return dict.py2hz[pinyin] || '';
},
/**
* 去除拼音中的声调,比如将 xiǎo míng tóng xué 转换成 xiao ming tong xue
* @param pinyin 需要转换的拼音
*/
removeTone: function(pinyin)
{
var toneMap =
{
"ā": "a1",
"á": "a2",
"ǎ": "a3",
"à": "a4",
"ō": "o1",
"ó": "o2",
"ǒ": "o3",
"ò": "o4",
"ē": "e1",
"é": "e2",
"ě": "e3",
"è": "e4",
"ī": "i1",
"í": "i2",
"ǐ": "i3",
"ì": "i4",
"ū": "u1",
"ú": "u2",
"ǔ": "u3",
"ù": "u4",
"ü": "v0",
"ǖ": "v1",
"ǘ": "v2",
"ǚ": "v3",
"ǜ": "v4",
"ń": "n2",
"ň": "n3",
"": "m2"
};
return pinyin.replace(/[āáǎàōóǒòēéěèīíǐìūúǔùüǖǘǚǜńň]/g, function(m){ return toneMap[m][0]; });
}
};
/**
* 处理多音字,将类似['D', 'ZC', 'F']转换成['DZF', 'DCF']
* 或者将 ['chang zhang', 'cheng'] 转换成 ['chang cheng', 'zhang cheng']
*/
function handlePolyphone(array, splitter, joinChar)
{
splitter = splitter || '';
var result = [''], temp = [];
for(var i=0; i<array.length; i )
{
temp = [];
var t = array[i].split(splitter);
for(var j=0; j<t.length; j )
{
for(var k=0; k<result.length; k )
temp.push(result[k] (result[k]?joinChar:'') t[j]);
}
result = temp;
}
return simpleUnique(result);
}
/**
* 根据词库找出多音字正确的读音
* 这里只是非常简单的实现,效率和效果都有一些问题
* 推荐使用第三方分词工具先对句子进行分词,然后再匹配多音字
* @param chinese 需要转换的汉字
* @param result 初步匹配出来的包含多个发音的拼音结果
* @param splitter 返回结果拼接字符
*/
function parsePolyphone(chinese, result, splitter, withtone)
{
var poly = window.pinyin_dict_polyphone;
var max = 7; // 最多只考虑7个汉字的多音字词,虽然词库里面有10个字的,但是数量非常少,为了整体效率暂时忽略之
var temp = poly[chinese];
if(temp) // 如果直接找到了结果
{
temp = temp.split(' ');
for(var i=0; i<temp.length; i )
{
result[i] = temp[i] || result[i];
if(!withtone) result[i] = pinyinUtil.removeTone(result[i]);
}
return result.join(splitter);
}
for(var i=0; i<chinese.length; i )
{
temp = '';
for(var j=0; j<max && (i j)<chinese.length; j )
{
if(!/^[\u2E80-\u9FFF] $/.test(chinese[i j])) break; // 如果碰到非汉字直接停止本次查找
temp = chinese[i j];
var res = poly[temp];
if(res) // 如果找到了多音字词语
{
res = res.split(' ');
for(var k=0; k<=j; k )
{
if(res[k]) result[i k] = withtone ? res[k] : pinyinUtil.removeTone(res[k]);
}
break;
}
}
}
// 最后这一步是为了防止出现词库里面也没有包含的多音字词语
for(var i=0; i<result.length; i )
{
result[i] = result[i].replace(/ .*$/g, '');
}
return result.join(splitter);
}
// 简单数组去重
function simpleUnique(array)
{
var result = [];
var hash = {};
for(var i=0; i<array.length; i )
{
var key = (typeof array[i]) array[i];
if(!hash[key])
{
result.push(array[i]);
hash[key] = true;
}
}
return result;
}
pinyinUtil.parseDict();
pinyinUtil.dict = dict;
window.pinyinUtil = pinyinUtil;
})();