cnchar:一个搞定拼音、笔画、成语的前端中文处理工具库
简介这份资源是一套功能全面、多端支持的汉字拼音笔画 JavaScript 工具包对应开源库 cnchar面向 Web 应用开发者尤其适合中文教育、文化展示和语言学习类项目。核心能力覆盖汉字拼音全拼、简拼、声调标注、笔画数与笔画顺序、偏旁部首、成语拼音解释并提供语音合成与可视化绘制方便在浏览器和 Node.js 环境下无缝调用。压缩包共 304 个文件大小 1.51MB主要包含 TypeScript 源码126 个 ts、JavaScript 示例与构建文件49 个 js、配置与元数据53 个 json、Markdown 文档28 个 md另有 HTML 演示页面、Vue 组件及少量样式和字体资源目录结构清晰便于开发者阅读源码、查看示例或直接集成到工程中。包内还附有完整的 API 说明和扩展插件机制适合需要深度定制中文处理功能的开发者参考。目前已有 216 人浏览学习作为轻量级工具包能减少从零实现拼音、笔画等功能的成本是一份实用且具备教学价值的中文前端开发资源。1. 为什么把拼音、笔画、成语交给 cnchar 而不是自己造轮子在 Web 应用里做中文搜索、拼音标注或者生字卡片时很多团队的第一反应是拿 Unicode 码表自己拼从\u4e00到\u9fa5判断汉字范围再手抄一份拼音对照表。这种办法在小页面上够用一旦要处理多音字、输出带声调的全拼、统计繁体笔画数组和正则就会把业务代码撑得很难看。cnchar 是一个把汉字拼音、笔画、偏旁、成语、语音合成和笔画动画都收纳进来的 JavaScript 工具包浏览器和 Node.js 都能跑。对做 web 应用开发的工程师来说它的价值不是省掉一张码表而是把中文数据拆成可组合的 API让拼音检索、识字教学、成语解释这类功能直接进业务代码而不是散落在十几个工具函数里。2. 拼音 API从全拼到多音字参数要这样用2.1 先用最少代码跑通全拼和简拼cnchar 的拼音能力可以只靠三个方法覆盖大部分场景pinyin()是统一入口pinyinFull()输出带声调全拼pinyinSimple()输出去掉声调后的拼音。先看最小可运行示例import cnchar from cnchar; import cnchar-pinyin; const full cnchar.pinyinFull(中文); console.log(full); // [zhōng, wén] const simple cnchar.pinyinSimple(中文); console.log(simple); // [zhong, wen]这段代码里cnchar.pinyinFull()返回的是数组每个元素对应输入字符串中的一个汉字声调以ā á ǎ à这种形式直接附着在韵母上。cnchar.pinyinSimple()则把声调符号剥掉适合做拼音搜索索引。要注意返回值始终是数组不是字符串所以直接拼接时记得调用.join()如果需要逐字渲染注音卡片就按数组下标与文字逐一对位。这三个方法的使用差异不只在声调还在你后续打算怎么消费数据。我通常把它们的调用约定固定成下面这张表避免团队成员在代码里各写各的风格函数返回内容典型用途cnchar.pinyin(text)按配置输出拼音数组统一入口后续按参数切换格式cnchar.pinyinFull(text)带声调全拼注音卡片、TTS 前置处理cnchar.pinyinSimple(text)去声调拼音搜索索引、URL 别名、内部匹配cnchar.pinyin()本身支持通过参数改变输出形态常见做法是在函数第二个参数里传一个对象控制是否返回数组、是否保留声调、是否只取声母。比如一段文本既要在页面上展示完整拼音又要生成搜索用的简写可以用同一个入口切换两次而不是维护两套拼音表。注意不要用字符串拼接代替数组操作。中文分词后每个字的位置很关键一旦把拼音和原文字数对不上后面做搜索结果高亮会很痛苦。2.2 多音字让词组先于单字进入 API拼音库最容易翻车的地方就是多音字。“重庆”的“重”读chóng“重量”的“重”读zhòng如果只把单字丢进 API结果大概率不是你想要的那个。cnchar 的处理方式依赖词表把多音字放到常见词里识别命中率会高很多。一个实际例子const text 重庆的解放碑; const fullList cnchar.pinyinFull(text); console.log(fullList); // [chóng, qìng, de, jiě, fàng, bēi]输入是整句而不是单字cnchar会按内部词典切分词边界“重庆”作为一个整体被识别所以“重”拿到了chóng这个读音。如果业务里有明确的地名词典比如“重庆”必须读chóng我建议在进 cnchar 之前先把词组边界切好或者在得到结果后用业务词典覆盖而不是依赖库去猜。部分版本还暴露了类似poly的扩展参数用来返回一个多音字的全部读音。这个功能适合做离线注音工具不适合直接进线上搜索流程因为把多个读音都返回后你仍然需要自己选一个作为主读音。生产环境更可控的做法是优先传完整词语让库的主词典决定读音遇到低频地名和人名时单独维护一个小字典在 cnchar 结果上做二次修正。2.3 拼音索引清洗和搜索场景的落地处理完读音之后真正进搜索库的常常是一串没有声调、没有特殊符号的纯字母。拼音里还有ü这类字符在 URL 和数据库索引里容易出问题。下面这个函数是我在项目里常用的清洗方式function toSearchIndex(text) { return cnchar .pinyinSimple(text) .join() .replace(/ü/g, v) .replace(/[^a-z]/g, ) .toLowerCase(); } console.log(toSearchIndex(长沙)); // changsha这里先调用pinyinSimple()拿到去声调拼音数组再join()变成连续字符串。replace(/ü/g, v)是为了解决“绿、吕”这类拼音在输入法里习惯写作lv的问题。随后把所有非a-z字符过滤掉最后统一转小写。这样清洗出来的索引适合放到内存 Map、Redis 或者 Elasticsearch 里做前缀匹配。如果你的搜索要支持中文和拼音混输比如用户输入changsha也能搜到“长沙”那就在建索引时同时保存中文原文字段和拼音索引字段。查询时用输入关键字分别匹配这两个字段同时把用户输入的字母全部转小写不需要在查询阶段再做繁体转换因为拼音索引已经帮你绕开了繁简体差异。3. 笔画、偏旁与成语cnchar 的数据形态和调用范式3.1 笔画数查询和繁简入口笔画数据是汉字库里的另一层信息cnchar 把它和拼音放在同一套 API 体系里。直接传入文字就能拿到笔画数。多字输入返回数组单字输入通常直接返回数字import cnchar from cnchar; import cnchar-trad; const multiCounts cnchar.stroke(中文); console.log(multiCounts); // [4, 4] const singleCount cnchar.stroke(中); console.log(singleCount); // 4代码里multiCounts是一个数组顺序与输入字符一一对应。“中”是 4 画“文”也是 4 画所以得到[4, 4]。这里要注意的是不要默认所有版本都返回数字某些封装可能会把单字结果也包成数组。稳妥做法是先在控制台打印一次返回值再决定后续是用reduce求和还是直接展示。cnchar 还通过trad()和simp()分别提供繁体字与简体字的笔画数据入口。比如繁体页面里查询“龍”的笔画可以直接调用cnchar.trad(龍)简体页面用cnchar.simp(龙)。这听起来只是换了一个查询函数实际价值是繁简两套数据不会互相污染页面切换语言时不需要把文本先转码再查笔画。调用预期返回使用场景cnchar.stroke(中)4生字卡显示总笔画cnchar.stroke(中文)[4, 4]批量文字排版cnchar.trad(龍)繁体笔画数据繁体内容处理cnchar.simp(龙)简体笔画数据简体内容处理这套接口的边界要分清楚stroke()负责查当前传入文字的笔画trad()与simp()负责指定数据方向。不要在调用stroke()时同时传繁简标识那样会让返回值变得不可预测排错时也很难判断是字形问题还是插件缺失。3.2 part 部首查询从“字”到“部件”的拆解cnchar.part()返回汉字的偏旁部首信息这是识字类 Web 应用很依赖的能力。比如用户在查“想”字时我们希望展示“心字底”同时告诉用户这个字由“相”和“心”组成。代码层面只需要一次调用const partInfo cnchar.part(想); console.log(partInfo);输出结果里通常包含部首本身、剩余部件以及它们的位置关系。不同版本返回的结构可能不完全一样所以接入时别急着写死字段先把返回对象打印出来确认part、rest这些字段存在后再做渲染。在实际项目里我会用它做两个方向的扩展一是把部首作为汉字卡片的分组索引例如将所有心字底的汉字归到一组二是把返回的部件信息转成 SVG 结构方便前端拼字。后者对数据结构要求更高建议在 cnchar 返回结果之上再加一层自己的映射不要直接依赖库内部字段名。3.3 idiom 成语数据结构化的解释比字符串更值钱成语接口让 cnchar 从单字处理工具变成内容数据源。cnchar.idiom()可以直接拿到成语的拼音和解释配合插件机制注册后在 Node 和浏览器里都能使用import cnchar-idiom; const idiom cnchar.idiom(一石二鸟); console.log(idiom.pinyin); console.log(idiom.explanation);这里pinyin是成语整句的拼音explanation是对应的中文解释。两者都是结构化字段适合直接展示在学习页面上。如果你要做成语填空或成语接龙也可以把成语先拆成单个汉字再分别调用pinyinFull()和stroke()实现多维度索引。这个接口提醒我一点成语数据尽量保持整词单元不要在一开始就把成语拆成单字再拼回来。像“一石二鸟”这类成语里有很多多音字和变调整词查询的准确性远高于逐字拼接。4. 语音合成、笔画绘制与多端兼容把 cnchar 接入 Web 应用的最后一步4.1 在浏览器里接 TTS 和笔画动画cnchar 的可视化和语音能力需要配合 DOM 使用。cnchar.tts()依赖浏览器底层的语音合成能力cnchar.draw()则负责把汉字笔画渲染成可播放的动画。一个典型调用如下import cnchar from cnchar; import cnchar-draw; import cnchar-tts; const box document.querySelector(#strokeBox); cnchar.draw(永, { el: box, animation: true }); cnchar.tts(你好);cnchar.draw()要传入一个已经挂载的 DOM 节点动画会直接渲染在节点内部。移动端浏览器对自动播放有限制cnchar.tts()最好放在用户点击按钮的事件回调里调用否则可能没有声音输出。draw()也是一样不要在数据请求还没回来时就执行避免拿到空容器。4.2 Node 侧只留数据能力在 Node.js 环境里拼音和笔画的功能可以照常使用但draw和tts不应该出现在服务端代码里。服务端更适合用 cnchar 做批量数据清洗比如给文章批量生成拼音索引。安装时按需引入npm install cnchar cnchar-pinyin然后在一个 Node 脚本里调用const cnchar require(cnchar); require(cnchar-pinyin); const pinyin cnchar.pinyinSimple(中文).join(); console.log(pinyin); // zhongwen这里只加载了拼音插件没有加载需要 DOM 的模块。好处是服务端打包体积更小也不会因为缺少window对象而报错。如果服务端和浏览器端共用同一份拼音结果建议把结果序列化后放入缓存而不是每次请求都重新计算。4.3 用断言锁住核心行为升级 cnchar 或调整插件顺序后最容易出现的问题是结果结构变化。简单写几个断言可以提前发现问题const assert require(assert); assert.ok(Array.isArray(cnchar.pinyinFull(中文))); assert.ok(cnchar.stroke(中) 0);第一行确认拼音返回数组第二行确认笔画数存在。注意不要写成cnchar.pinyinFull(中文) [zhōng, wén]因为数组比较需要先JSON.stringify或者用deepStrictEqual。一旦断言失败先检查插件是否注册完整再打印返回值确认数据结构。最后接cnchar.draw()时给容器固定宽高并用overflow: hidden包一层可以避免低版本 WebView 在笔画动画播放时把布局撑跳动画结束后移除遮罩比直接监听内部回调更稳。本文还有配套的精品资源点击获取