正文
需求
此前项目中有一个中文转拼音的需求,于是整理了一下实现方法。
在转换过程中,这里我保留了数字和字母,其他特殊字符或者空格将会被去掉。如
测试
会被转换成CeShi
。(可自行修改convertToPinyin()
方法实现其他需求)
实现
话不多说(Github Demo)
<!-- html --> <input id="input" value="测试" /> <input id="output" disabled />
// js window.onload = () => { const input = document.getElementById('input') const output = document.getElementById('output') // 失焦进行转换 input.addEventListener('blur', () => { const val = input.value output.value = convertToPinyin(val) }) /** * 转换成拼音(若有空格、特殊字符将被移除) * * @param {string} sourceStr 原始数据 */ const convertToPinyin = sourceStr => { // 目标数据 let targetStr = '' // 匹配中文 const cnReg = /[\u4e00-\u9fa5]/ // 匹配数字和英文 const enReg = /[a-zA-Z0-9]/ // 保留英文和数字 const keep = true // 遍历源数据 for (let i = 0, len = sourceStr.length; i < len; i++) { const str = sourceStr.substr(i, 1) if (keep && enReg.test(str)) { targetStr += str } else if (cnReg.test(str)) { const searchResult = searchPinYin(str, PinYin) if (searchResult) { // targetStr += searchResult targetStr += firstCapital(searchResult) // 首字母大写 } } } return targetStr } /** * 检索拼音 * * @param {string} str 源字符串 * @param {object} data 收集的拼音 Unicode 编码集合 */ const searchPinYin = (str, data) => { for (const key in data) { if (data.hasOwnProperty(key) && data[key].indexOf(str) !== -1) { return key } } return '' } /** * 将拼音首字母转换为大写 * * @param {string} str 源字符串 */ const firstCapital = str => { if (str) { const [first] = str const other = str.replace(/^\S/, '') return `${first.toUpperCase()}${other}` } return str } /** * 目前这个 16 进制 Unicode 编码是网上收集的,可能不能覆盖所有的中文字符,可以自行补充; * * 例如:‘婋’(xiao)字: * 1、使用 '婋'.charCodeAt(0).toString(16) 得到 Unicode 编码:5a4b; * 2、将编码前面加上:\u => \u5a4b; * 3、然后放到对象 PinYin['xiao'] 里面。 * * 现在只想到了这种笨方法一个一个往里补充,如果有更好的方法,欢迎指出!!! */ const PinYin = { a: '\u554a\u963f\u9515' // ... // 由于这块代码太多,这里省略就不贴上来了,麻烦请看 GitHub Demo。 } }
难点
其实在中文转拼音的过程中,比较麻烦的在于多音字
和生僻字
的实现,我想到的解决思路是:
- 生僻字:是由于
PinYin
里面列举的缺失一些中文字符,可通过str.charCodeAt(0).toString(16)
的方式补充,具体方法不再赘述,上面有说明。 - 多音字:其实多音字一直是最麻烦的地方,因为每一个中文字符只有一个对应的 Unicode 编码,所以需要在对象的多个对应属性上添加编码。
如曾
字,通过'曾'.charCodeAt(0).toString(16)
获取到66fe
,然后前面拼接上\u
,得到\u66fe
。
// 截取 PinYin 一小部分 const PinYin = { ceng: '\u66fe', zeng: '\u66fe' }
然后修改上述 convertToPinyin()
和 searchPinYin()
方法,把符合规则的字符返回一个数组,然后用排列组合的方式列出所有可能。