| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 | /** * 汉字与拼音互转工具,根据导入的字典文件的不同支持不同 * 对于多音字目前只是将所有可能的组合输出,准确识别多音字需要完善的词库,而词库文件往往比字库还要大,所以不太适合web环境。 * @start 2016-09-26 * @last 2016-09-29 */;(function(global, factory) {	if (typeof module === "object" && typeof module.exports === "object") {		module.exports = factory(global);	} else {		factory(global);	}})(typeof window !== "undefined" ? window : this, function(window) {	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"	};	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++)					{						if(!dict.notone[temp[j]]) dict.notone[temp[j]] = i; // 不考虑多音字					}				}			}			// 如果导入了 pinyin_dict_withtone.js			if(window.pinyin_dict_withtone)			{				dict.withtone = {}; // 汉字与拼音映射,多音字用空格分开,类似这种结构:{'大': 'da tai'}				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); // 汉字						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) // 优先使用带声调的字典文件			{				var noChinese = '';				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); // 如果不需要声调						//空格,把noChinese作为一个词插入						noChinese && ( result.push( noChinese), noChinese = '' );						result.push( pinyin ); 					}					else if ( !chinese[i] || /^ +$/g.test(chinese[i]) ){						//空格,把noChinese作为一个词插入						noChinese && ( result.push( noChinese), noChinese = '' );					}					else{						noChinese += chinese[i];					}				}				if ( noChinese ){					result.push( noChinese);					noChinese = '';				}			}			else if(dict.notone) // 使用没有声调的字典文件			{				if(withtone) console.warn('pinyin_dict_notone 字典文件不支持声调!');				if(polyphone) console.warn('pinyin_dict_notone 字典文件不支持多音字!');				var noChinese = '';				for (var i=0, len = chinese.length; i < len; i++)				{					var temp = chinese.charAt(i),						pinyin = dict.notone[temp];					if ( pinyin ){ //插入拼音						//空格,把noChinese作为一个词插入						noChinese && ( result.push( noChinese), noChinese = '' );						result.push( pinyin );					}					else if ( !temp || /^ +$/g.test(temp) ){						//空格,插入之前的非中文字符						noChinese && ( result.push( noChinese), noChinese = '' );					}					else {						//非空格,关联到noChinese中						noChinese += temp;					}				}				if ( noChinese ){					result.push( noChinese );					noChinese = '';				}			}			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[this.removeTone(pinyin)] || '';		},		/**		 * 获取某个汉字的同音字,本方法暂时有问题,待完善		 * @param hz 单个汉字		 * @param sameTone 是否获取同音同声调的汉字,必须传进来的拼音带声调才支持,默认false		 */		getSameVoiceWord: function(hz, sameTone)		{			sameTone = sameTone || false			return this.getHanzi(this.getPinyin(hz, ' ', false))		},		/**		 * 去除拼音中的声调,比如将 xiǎo míng tóng xué 转换成 xiao ming tong xue		 * @param pinyin 需要转换的拼音		 */		removeTone: function(pinyin)		{			return pinyin.replace(/[āáǎàōóǒòēéěèīíǐìūúǔùüǖǘǚǜńň]/g, function(m){ return toneMap[m][0]; });		},		/**		 * 将数组拼音转换成真正的带标点的拼音		 * @param pinyinWithoutTone 类似 xu2e这样的带数字的拼音		 */		getTone: function(pinyinWithoutTone)		{			var newToneMap = {};			for(var i in toneMap) newToneMap[toneMap[i]] = i;			return (pinyinWithoutTone || '').replace(/[a-z]\d/g, function(m) {				return newToneMap[m] || m;			});		}	};	/**	 * 处理多音字,将类似['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;});
 |