
简介这份汉字拼音对照数据包面向中文信息处理学习者、语言教学者及需要拼音数据的开发者提供常用汉字与汉语拼音的批量匹配基础可用于发音查询、输入法词库构建、语音识别预处理及语言统计等场景。压缩包共3个文件以txt纯文本、json结构化数据和Python脚本为主分别承担易读易改的原始拼音表、便于程序解析的数据交换格式以及txt转json的转换处理整体约174KB轻量但覆盖两万余汉字拼音条目。已有507人学习下载。读者可获得一份可直接检索与二次开发的拼音库借助脚本快速完成格式转换并把数据接入教学工具或自然语言处理流程同时也能参考其字段组织方式进行拼音校正、字典生成和用字分布分析减少从零整理数据的时间成本。1. 汉字对应拼音资源txt 与 json 两套表先解决“能读、能查、能改”做中文检索、通讯录排序或输入提示时最先卡住的往往不是算法而是手上没有一份能直接读的汉字对应拼音表。汉语拼音看着简单真落到数据层声调、多音字、ü 的写法、txt 与 json 两套格式的字段对齐每一项都能让后面的批量处理翻车。这份资源把汉字读音整理成 txt 与 json 两种形态适合要做拼音索引、排序、关键词归一化的开发者也适合需要离线词表、不想每次跑转换的维护者。拿到后先别急着 import先确认编码、分隔符和声调表示这三件事决定它能不能直接进流水线也决定你后面排查问题时是在查业务还是在查脏数据。2. 数据形态先定txt 源表与 json 产物的字段设计、编码与多音字表示2.1 两种格式的适用边界与字段约定txt 和 json 不是谁替代谁的关系。txt 的长处是逐行、可 diff、可 grep人工抽检时一眼能看出哪一行读音不对短处是字段一多就要靠分隔符约定多读音、备注、来源标记全挤在一行里改错一个 tab 就整列错位。json 的长处是结构明确readings 可以直接用数组程序 load 进来就能按字段取短处是整份文件格式化后 diff 噪音大手工改一个读音容易带出无关行变更。我一般把 txt 当“源表”把 json 当“构建产物”。源表给人改产物给程序读。反过来也能跑但维护成本会高每次修一个多音字都要动整份 json合并冲突时很难判断是数据变了还是格式变了。字段约定按下面这套走txt 和 json 能对得上字段类型必填说明示例charstring是单个汉字UTF-8重readingsarray[string]是拼音数组统一小写带调[zhong4,chong2]sourcestring否来源标记便于回溯auto / manualnotestring否多音字备注、修订说明人工确认txt 每行按char\treading1|reading2\tnote组织第一列单字第二列多读音用竖线分隔第三列备注。字段内禁止出现 tab 和换行否则解析必翻车。json 用数组而不是对象[ { char: 重, readings: [zhong4, chong2], source: manual, note: 多音字保留全部读音 } ]这里有个容易忽略的选择为什么不用{重: [zhong4,chong2]}这种对象映射对象键重复时解析器会静默覆盖后面那个把前面那个吃掉键的顺序也不稳定做差异对比时全是噪音。数组结构多一层但合并、去重、加字段都更稳。多读音统一放数组里下游要默认读音就自己取第一个但“取第一个”这个策略必须写进配置不能散落在各处代码里。2.2 编码、分隔符与声调标记的硬约束编码统一 UTF-8 无 BOM。Windows 记事本另存时可能带 BOM读进来第一行首字会变成\ufeff重你拿“重”去匹配永远匹配不上。常见做法是读的时候用utf-8-sig兼容 BOM写的时候用utf-8不带 BOM不要两种编码混着提交。换行统一\n提交前跑一次字节检查确认没有\r\n混入。分隔符用制表符不用逗号、空格或竖线做字段分隔。竖线只用于 readings 内部这样字段边界和读音边界不会打架。拼音里本身可能带空格分词结果所以字段内要先去空格再落库。下面这段检查脚本放在清洗之前跑能提前发现大半格式问题from pathlib import Path p Path(data/raw/hanzi_pinyin.txt) raw p.read_bytes() print(BOM:, raw[:3] b\xef\xbb\xbf) print(CRLF:, b\r\n in raw) print(含TAB行数:, sum(1 for line in raw.splitlines() if b\t in line)) print(总行数:, len(raw.splitlines()))raw[:3]判断 UTF-8 BOMb\r\n in raw判断 Windows 换行含 TAB 行数用来确认分隔符没有被编辑器替换成空格。这几个指标不正常后面的解析脚本再健壮也救不回来。声调表示二选一不能混存。数字声调写成zhong4、lv4符号声调写成zhòng、lǜ。落库建议用数字声调因为可排序、可正则、跨字体稳定展示层再转符号。轻声统一标5或不标必须全局一致不能一部分de5、一部分de。ü 的写法也要定死我一般存v入口把ü、u:都归一成v展示时再转回ü。这样不会出现“lü4”“lv4”“lu4”三份数据互相打架搜索时也不用写三套匹配。儿化要单独定规则。“儿”作为独立汉字时读er2儿化韵如“花儿”是标成hua1 er5还是合并成huar1取决于下游做不做儿化合并。不要一会儿拆一会儿合否则同一句话在不同模块里会得到不同拼音串。2.3 用脚本把原始词表清洗成统一结构清洗脚本做四件事去 BOM、跳空行和注释、按 tab 拆字段、合并重复汉字。合并时保留已有读音并去重顺序按首次出现避免每次构建产生无意义的行序变化。import json from pathlib import Path RAW Path(data/raw/hanzi_pinyin.txt) OUT Path(data/clean/hanzi_pinyin.json) def parse_line(line: str): line line.strip().lstrip(\ufeff) if not line or line.startswith(#): return None parts line.split(\t) if len(parts) 2: raise ValueError(f字段不足: {line!r}) char parts[0].strip() readings [r.strip().lower() for r in parts[1].split(|) if r.strip()] note parts[2].strip() if len(parts) 2 else if len(char) ! 1: raise ValueError(f不是单字: {char!r}) if not readings: raise ValueError(f读音为空: {char!r}) return {char: char, readings: readings, source: raw, note: note} records [] seen {} for lineno, line in enumerate(RAW.read_text(encodingutf-8-sig).splitlines(), 1): try: item parse_line(line) except ValueError as e: raise ValueError(f第{lineno}行: {e}) from e if not item: continue ch item[char] if ch in seen: # 重复字合并读音dict.fromkeys 去重且保序 merged list(dict.fromkeys(seen[ch][readings] item[readings])) seen[ch][readings] merged if item[note]: seen[ch][note] (seen[ch][note] ; item[note]).strip(;) else: seen[ch] item records.append(item) OUT.parent.mkdir(parentsTrue, exist_okTrue) OUT.write_text( json.dumps(records, ensure_asciiFalse, indent2) \n, encodingutf-8, ) print(写出条数:, len(records))encodingutf-8-sig在读取时自动吃掉 BOMlstrip(\ufeff)是双保险防止文件中间混入 BOM。split(\t)严格按制表符拆len(parts) 2直接报错并带行号比默默跳过更容易定位。dict.fromkeys用来去重且保留首次出现顺序读音合并不会因为重复行而膨胀。ensure_asciiFalse让 json 里保留汉字indent2便于 diff末尾补换行是为了避免某些工具把“无换行”当成变更。参数上RAW和OUT按你的目录改如果源表用逗号分隔把split(\t)换成split(,)但前提是确认拼音字段里没有逗号。source先标raw后面自动补全的字标auto人工改过的标manual回溯时能分清责任。清洗完不要直接上线先跑第 4 章的校验把格式错误挡在构建阶段。3. 从汉字到拼音批量转换流程与多音字消歧策略3.1 单字、词组、整句三条处理链路单字链路面对的是“一个字有哪些读音”。资源里的 readings 数组就是干这个的下游不能默认取第一个。比如“重”有zhong4和chong2“行”有xing2和hang2只取第一个会让“重复”读成 zhong4 fu4。单字查询要返回全部读音并允许调用方按业务选默认值。词组链路面对的是“这个词整体读什么”。常见拼音库自带词组词典词组匹配优先于单字拼接。“银行”读 yin2 hang2“行走”读 xing2 zou3靠单字默认值拼不出来。词组链路的关键是词典覆盖率和自定义词条业务里的专有名词、术语、产品名要进白名单不能指望通用词典全收。整句链路面对的是“上下文”。短句可以直接交给库做短语匹配长文本先按标点、空格、数字切段再对每段做词组匹配避免跨句歧义。三条链路的输出结构要统一建议都返回tokens: [{text, pinyin: []}]不要单字返回字符串、词组返回数组、整句返回嵌套结构下游会写一堆 if。我一般把三条链路收在一个入口函数里按输入长度和是否含标点分流。单字查表词组查词典整句切段后逐段查。入口处统一做字符归一化全角转半角、去零宽字符、把非汉字单独成 token。这样后面换库或换词典调用方不用改。3.2 多音字词典优先、上下文兜底、人工白名单多音字消歧的顺序我固定为四层人工白名单、词组词典、单字默认、上下文模型。白名单最高业务里确认过的词直接锁定读音自动转换不许覆盖词组词典第二通用词按词典走单字默认第三只在没有词组命中时兜底上下文模型放最后成本高不要一上来就上。白名单表单独存一份字段用phrase、readings、note、updated_at。加载时先于自动转换执行命中就跳过。白名单要版本化改动必须跑回归否则今天修好“重复”明天可能把“重量”带偏。下面是一个最小加载示例具体词典格式按你用的库的文档来from pypinyin import load_phrases_dict, pinyin, Style # 自定义词组每个字一个拼音列表不带声调时由库补默认调 load_phrases_dict({ 银行: [[yin], [hang]], 重复: [[chong], [fu]], 音乐: [[yin], [yue]], }) print(pinyin(银行, styleStyle.TONE3)) print(pinyin(重复, styleStyle.TONE3))load_phrases_dict的键是词组值是按字拆开的拼音列表长度要和词组字数一致。词典里不带声调时库会按默认规则补调如果你要精确控制声调就在列表里写带调形式具体写法看库的约定。加载顺序很关键先加载通用词典再加载业务白名单后加载的覆盖先加载的。不要在循环里反复 load放进程启动时执行一次。上下文兜底不是必须的。除非你的业务里多音字错误率已经影响搜索质量否则先把白名单和词组词典做厚收益比上模型高。人工白名单看起来笨但它可解释、可回滚出问题能定位到具体词条这就是生产环境需要的确定性。3.3 批量转换脚本与增量更新全量转换两万多个汉字每次都重跑既慢又容易覆盖人工修订。我一般做增量先加载已有 json已有字保留只补缺失字。自动补的标source: auto人工改过的标manual后续构建不覆盖manual。import json from pathlib import Path from pypinyin import pinyin, Style BASE Path(data/clean/hanzi_pinyin.json) rows json.loads(BASE.read_text(encodingutf-8)) if BASE.exists() else [] known {r[char]: r for r in rows} def readings_for_char(ch: str): # heteronymTrue 取全部读音errorsignore 跳过非汉字 return [x[0] for x in pinyin(ch, styleStyle.TONE3, heteronymTrue, errorsignore)] added 0 for cp in range(0x4E00, 0x9FFF 1): ch chr(cp) if ch in known: continue rs readings_for_char(ch) if not rs: continue rows.append({char: ch, readings: rs, source: auto, note: }) known[ch] rows[-1] added 1 BASE.write_text( json.dumps(rows, ensure_asciiFalse, indent2) \n, encodingutf-8, ) print(新增:, added, 总条数:, len(rows))0x4E00到0x9FFF是常用汉字码点范围按需扩到扩展区。heteronymTrue对单字取全读音这是资源的核心价值如果对词组也开异读可能把词组拆出多余读音所以词组链路用heteronymFalse让词典优先。errorsignore丢掉标点和符号避免把非汉字写进表里。增量脚本解决的是“补字”不解决“改音”。人工修订走另一条路在白名单或源表里改重新跑清洗再用第 4 章的差异报告确认只动了预期条目。不要把自动补全和人工修订混在一个脚本里否则一次全量重跑就可能把手工修正冲掉这种翻车现场我见过不止一次。4. 校验与回归怎么确认这份拼音表没有“看起来对”的错4.1 抽样校验与全量一致性检查拼音表的错误分两类一类是格式错程序能抓一类是读音错程序抓不到只能靠抽样。格式错靠全量校验读音错靠分层抽样。抽样不要随机抽按拼音首字母分层每层抽固定数量重点抽多音字、生僻字、异体字和业务高频词。多音字是重灾区抽检时把 readings 数组整个打印出来不要只看第一个。全量检查至少覆盖这些项检查项规则失败处理单字长度char 长度必须为 1报错并给行号读音非空readings 至少 1 个报错并给汉字字符集只允许字母和声调数字报错并给拼音声调范围数字声调只允许 1-5报错并给拼音重复字char 不得重复合并或报错编码UTF-8 无 BOM重新导出json 可解析能正常 load修格式全量校验脚本不要只打印错误条数要打印前 N 条明细和对应行号否则两万条里错三条你根本不知道从哪查。4.2 声调、轻声、儿化与 ü 的落库规则落库前先归一化再校验。归一化函数要做四件事转小写、去空格、u:和ü转v、去掉多余分隔符。展示层再反向转回ü和符号声调。下面这个函数放在所有写库入口之前def normalize_pinyin(p: str) - str: p p.strip().lower() p p.replace(u:, v).replace(ü, v) p p.replace( , ) return p for raw in [Lǜ, lu:4, lü4 , ZHONG4]: print(raw, -, normalize_pinyin(raw))replace(u:, v)处理键盘输入习惯replace(ü, v)处理符号形式replace( , )去掉分词空格。归一化之后再做正则校验规则会简单很多。轻声如果统一标 5就在归一化后把没有声调数字的音节补5如果选择不标就禁止出现5。两种策略只能选一种且要写进字段说明。儿化的处理要在归一化之前决定拆开存就保留er5作为独立音节合并存就在词组层做huar1映射。不要在单字表里把“儿”改成r那会污染单字读音。ü 的展示转换只在输出层做不要把v又写回库里否则下一轮归一化会重复处理。4.3 用差异报告定位脏数据每次重新构建 json都跟上一版做差异报告。不要只看条数条数一样也可能读音被改。报告分三块新增字、删除字、读音变更字。读音变更要打印前后值方便判断是自动转换的波动还是人工修订。import json from pathlib import Path def load_map(path): data json.loads(Path(path).read_text(encodingutf-8)) return {r[char]: r[readings] for r in data} old load_map(data/clean/hanzi_pinyin.prev.json) new load_map(data/clean/hanzi_pinyin.json) added sorted(set(new) - set(old)) removed sorted(set(old) - set(new)) changed sorted(k for k in set(old) set(new) if old[k] ! new[k]) print(新增:, len(added), added[:20]) print(删除:, len(removed), removed[:20]) print(变更:, len(changed)) for k in changed[:20]: print(k, old[k], -, new[k])set(new) - set(old)找新增反向找删除交集里比值找变更。打印时截断前 20 条避免刷屏完整报告写文件提交时附在构建日志里。如果变更数突然从几十跳到几千先别合并多半是归一化规则改了或词典加载顺序变了。差异报告是拼音表的黑匣子出问题能往回倒没有它就只能靠记忆猜。5. 避坑与排查五条血泪经验5.1 首字匹配不上BOM 和编码在捣乱现象txt 第一行明明是“重”程序读进来匹配不到打印出来前面多个看不见的字符。原因文件带 UTF-8 BOM或者编辑器把文件存成了 GBK 再被当 UTF-8 读。解决读的时候用utf-8-sig写的时候用utf-8提交前跑字节检查确认raw[:3] ! b\xef\xbb\xbf。如果是 GBK 文件先转码再清洗不要在原文件上反复另存越存越乱。5.2 多音字只剩一个读音默认值取太早现象“重”永远返回 zhong4“行”永远返回 xing2词组注音跟着错。原因代码里写了readings[0]或者在转换时没开异读开关。解决单字查询用heteronymTrue取全读音词组查询让词典优先落库保留数组默认值策略放到调用方并且写进配置。已经上线的表用差异报告找出 readings 长度从 2 变 1 的字逐个回补。5.3 json 键重复后一个把前一个吃掉现象源表里“长”有 chang2、zhang3 两个读音加载 json 后只剩一个。原因用了对象映射{长: [...]}解析时同名键覆盖。解决改成数组结构每个元素带 char 和 readings如果历史数据已经是对象加载时先转成列表并合并同名键的读音再去重。合并逻辑要幂等跑两遍结果一样否则每次构建都在漂移。5.4 txt 列错位分隔符和字段内容打架现象某几行解析出来 char 是空的或者 readings 里混进了备注。原因拼音字段里带了空格或逗号被当成分隔符或者编辑器把 tab 转成了空格。解决字段分隔只用 tabreadings 内部只用竖线清洗前检查含 tab 行数发现空格分隔直接报错备注里禁止 tab 和换行需要多行说明就另起字段或写进 json。5.5 声调与 ü 混存搜“lv”搜不到“lü”现象用户输入 lv4 查不到“绿”或者同一份表里lü4、lv4、lu4同时存在。原因不同来源的拼音写法没归一入口也没做转换。解决落库统一存v入口把ü、u:都转v展示层再转回ü查询时对关键词做同样的归一化。归一化函数要只有一份不要每个模块各写一套否则修了搜索修不了排序。6. 进阶用法把拼音表接进检索、排序与输入提示6.1 拼音首字母索引与排序通讯录和商品列表最常见的需求是按拼音首字母分组。单字取声母时要注意 zh、ch、sh 是双字母声母不能简单取拼音第一个字符否则“中”会被分到 z 组而不是 zh 组。常见做法是用拼音库的 INITIALS 风格strict参数控制是否保留完整声母按你的索引粒度选from pypinyin import lazy_pinyin, Style def initial_of(ch: str) - str: s lazy_pinyin(ch, styleStyle.INITIALS, strictTrue, errorsignore) return s[0] if s else # print(initial_of(中), initial_of(行), initial_of(绿))strictTrue时返回完整声母适合 zh/ch/sh 分组strictFalse返回首字母适合 A-Z 大分组。两种粒度的索引可以同时建展示用大分组搜索用小分组。排序时不要直接对带调拼音字符串排序先把声调数字拆出来按“无调音节 声调数字 汉字”做元组键避免zhong1和zhong10这种不存在的歧义也让同音字有稳定顺序。6.2 用前缀树做输入提示输入提示的本质是“给拼音前缀返回候选汉字”。把每个汉字的全部读音插进前缀树节点上挂汉字列表查询时沿前缀走走到节点后收集子树里的字限制返回数量。多音字每个读音都插一遍这样输入chong和zhong都能出“重”。class TrieNode: __slots__ (children, chars) def __init__(self): self.children {} self.chars [] def build_trie(rows): root TrieNode() for row in rows: for py in row[readings]: node root for c in py: node node.children.setdefault(c, TrieNode()) node.chars.append(row[char]) return root def query(root, prefix, limit20): node root for c in prefix: if c not in node.children: return [] node node.children[c] out, stack [], [node] while stack and len(out) limit: n stack.pop() out.extend(n.chars) stack.extend(n.children.values()) return out[:limit]__slots__减少节点内存setdefault建子节点查询用栈做深度优先凑够limit就停。生产环境要按词频或拼音长度排序不要按字典序否则候选质量很差。前缀树构建一次常驻内存更新词表时整体重建不要边查边改。6.3 版本化与回滚拼音表是基础数据改错一个读音搜索、排序、提示全跟着错。我习惯每次构建记录三样东西输入文件哈希、输出条数、差异报告摘要。输出文件名带日期或版本号保留上一版出问题直接回滚不用从 git 历史里翻。从那以后我每次改拼音表都先跑差异报告再合并不给自己留后悔药。这份资源拿到后先按第 2 章的字段约定对一遍源表再跑第 4 章的校验最后用差异报告盯住变更基本能避开大部分返工。希望帮到你。本文还有配套的精品资源点击获取