forked from sxei/pinyinjs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pinyinUtil.js
307 lines (301 loc) · 9.07 KB
/
pinyinUtil.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/**
* 汉字与拼音互转工具,根据导入的字典文件的不同支持不同
* 对于多音字目前只是将所有可能的组合输出,准确识别多音字需要完善的词库,而词库文件往往比字库还要大,所以不太适合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;
})();