简介:这份GB2312标准字库以JSON格式整理,面向从事中文信息处理、字体开发、输入法设计或字符编码研究的开发者与学习者,用于解决汉字字符集在程序中的快速检索、映射与校验问题。资源包共1个文件,为单个json数据文件,压缩包整体约8KB,体量轻便,便于直接嵌入项目或作为编码对照表使用。数据以编号与汉字一一对应的结构组织,覆盖GB2312标准中的常用汉字,可支撑字符排序、字库校验、编码转换等场景。目前已有123人学习下载,适合需要轻量级字库数据、希望避免自行整理编码表的开发者参考使用,也可作为教学演示中字符集概念的实例素材。
1. 从一份 GB2312标准字库.json 说起:点阵字模到底怎么存、怎么用
做嵌入式 UI、老式点阵屏或者票据打印的人,迟早会撞上一件事:手里只有一份 GB2312标准字库.json,却不知道它里面那一串串数字到底代表什么,更不知道怎么把它变成屏幕上能看的汉字。我第一次拿到这种文件时也懵过——打开一看全是十六进制数组,既没有宽高说明,也没有偏移表,纯靠猜。后来才明白,这类 JSON 本质上是把 GB2312 的区位码、点阵位图、字宽字高打包成结构化数据,方便前端或脚本直接读取,而不用去啃二进制字库文件。它能解决的核心问题是:让你在浏览器、Node 脚本或轻量嵌入式环境里,用纯文本方式拿到汉字点阵,做渲染、做校验、做字模提取。适合谁?做 HMI 界面的、搞 LED 屏驱动的、写打印固件的,以及需要批量生成字模数组的工程师。下面我按“先看懂结构、再跑通渲染、最后避坑”的顺序,把这份 JSON 拆开讲透。
2. 拆开 GB2312标准字库.json:字段含义与点阵编码规则
2.1 一份典型 JSON 的顶层结构长什么样
不同来源的 GB2312标准字库.json 字段命名会有差异,但核心信息逃不出这几类:字符编码标识、点阵宽高、每行字节数、位图数据。常见做法是用一个对象数组,每个元素对应一个汉字。我见过比较规范的一种结构是这样:
{ "font": "gb2312_16x16", "width": 16, "height": 16, "bytesPerRow": 2, "glyphs": [ { "code": "B0A1", "char": "啊", "bitmap": [ "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00", "0x00","0x00","0x00","0x00" ] } ] }这里code是 GB2312 编码的十六进制表示,char是方便人眼校验的字符,bitmap是逐行的字节数组。16×16 点阵每行占 2 字节,共 16 行,所以数组长度是 32。如果你拿到的 JSON 里 bitmap 长度对不上width/height/8,那基本可以判定文件被截断或字段定义有坑。
2.2 区位码、GB2312 编码与 Unicode 的三角关系
GB2312 用两个字节表示一个汉字,高字节和低字节都从 0xA1 开始。区位码则是把这两个字节各减 0xA0,得到 1~94 的区号和位号。比如“啊”的 GB2312 编码是 B0A1,区号 = 0xB0 - 0xA0 = 0x10 = 16,位号 = 0xA1 - 0xA0 = 1。所以“啊”在 16 区 1 位。
在 JSON 里,code字段通常直接给 B0A1 这种形式。你要做的是把它转成区位码去查表,或者反过来用区位码定位数组下标。很多字库 JSON 的 glyphs 数组是按区位码顺序排列的,下标计算公式是(区号 - 1) * 94 + (位号 - 1)。但注意,不是所有 JSON 都保证顺序,稳妥做法是先建一个 code 到 index 的映射。
# 建立 GB2312 编码到字模索引的映射 def build_code_index(glyphs): index = {} for i, g in enumerate(glyphs): index[g["code"].upper()] = i return index # 区位码转 GB2312 编码 def quwei_to_gb2312(qu, wei): return "{:02X}{:02X}".format(qu + 0xA0, wei + 0xA0)这段代码解决两个问题:一是快速根据编码找到字模,二是从区位码反推编码。参数上注意qu和wei范围是 1~94,超出就是非法区位。实际用的时候,我会先把 JSON 读进来,跑一遍校验,看有没有重复 code 或缺失区位。
2.3 点阵位图的字节序与位序:最容易翻车的地方
点阵数据最坑的不是编码,而是位序。同样一个 16×16 汉字,不同字库可能采用“高位在前”或“低位在前”,行序也可能从上到下或从下到上。GB2312标准字库.json 里如果没写清楚,你就得靠试。
常见约定是:每行第一个字节的 bit7 对应最左边像素,bit0 对应第 8 个像素;第二个字节 bit7 对应第 9 个像素。也就是 MSB 在左。但有些嵌入式字库会反过来,LSB 在左。判断方法很简单:找一个笔画简单的字,比如“一”,看它的 bitmap 哪一行有非零值,再对照位模式。
# 将一行字节按 MSB-left 展开成像素列表 def row_to_pixels(row_bytes, width): pixels = [] for b in row_bytes: for bit in range(7, -1, -1): pixels.append((b >> bit) & 1) return pixels[:width] # 示例:解析第一行 row = [0x10, 0x00] print(row_to_pixels(row, 16))这段代码把字节展开成 0/1 像素。如果你发现渲染出来的字是镜像的,就把range(7, -1, -1)改成range(0, 8)。参数width用来截断多余位,因为一行字节数可能按 8 的倍数补齐,实际宽度小于补齐宽度。
3. 用 Python 把 JSON 字模渲染成图片:从读取到验证的完整链路
3.1 环境准备与依赖选择
渲染字模不需要重型图形库。我一般用 Pillow 生成 PNG 做肉眼校验,用 numpy 做数组操作。安装就两条:
pip install pillow numpy如果你在纯前端环境,可以用 canvas 直接画,但 Python 更适合批量验证和生成对照图。选 Pillow 的理由是它 API 稳定,保存 PNG 方便,而且能直接放大像素,方便看细节。numpy 用来把点阵转成二维数组,做翻转、旋转都很快。
3.2 读取 JSON 并构建字模查找表
先写一个加载函数,把 JSON 读进来,同时做基本校验:宽高是否一致、bitmap 长度是否等于height * bytesPerRow、code 是否唯一。
import json def load_font(json_path): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) width = data["width"] height = data["height"] bytes_per_row = data.get("bytesPerRow", (width + 7) // 8) glyphs = data["glyphs"] code_map = {} for g in glyphs: code = g["code"].upper() if code in code_map: raise ValueError(f"重复编码: {code}") if len(g["bitmap"]) != height * bytes_per_row: raise ValueError(f"{code} 点阵长度不符") code_map[code] = g return { "width": width, "height": height, "bytesPerRow": bytes_per_row, "codeMap": code_map }这里bytesPerRow如果 JSON 没给,就按(width + 7) // 8算。校验点阵长度能提前发现文件损坏。codeMap用大写编码做键,避免大小写不一致。
3.3 把 bitmap 转成图像并保存
核心逻辑是逐行解析字节,展开成像素,再写入图像。为了看清点阵,我会把每个像素放大 10 倍保存。
from PIL import Image import numpy as np def render_glyph(font, code, scale=10): g = font["codeMap"].get(code.upper()) if not g: raise KeyError(f"未找到编码: {code}") w = font["width"] h = font["height"] bpr = font["bytesPerRow"] bitmap = g["bitmap"] img = np.zeros((h, w), dtype=np.uint8) for row in range(h): row_bytes = bitmap[row * bpr:(row + 1) * bpr] pixels = [] for b in row_bytes: for bit in range(7, -1, -1): pixels.append((b >> bit) & 1) img[row, :] = pixels[:w] # 0 变白,1 变黑,放大 out = Image.fromarray((1 - img) * 255).convert("L") out = out.resize((w * scale, h * scale), Image.NEAREST) return out font = load_font("GB2312标准字库.json") img = render_glyph(font, "B0A1") img.save("a.png")render_glyph里scale控制放大倍数,Image.NEAREST保证像素边缘锐利。(1 - img) * 255是把 1 当黑、0 当白,符合阅读习惯。保存后打开 a.png,如果看到的是“啊”字,说明位序和行序都对;如果看到镜像或倒置,就调整位序或行序。
3.4 批量导出与对照表生成
单个字验证通过后,批量导出常用字,拼成一张大图,方便快速检查字库整体质量。
def export_sheet(font, codes, cols=16, scale=4): imgs = [render_glyph(font, c, scale) for c in codes] w, h = imgs[0].size rows = (len(imgs) + cols - 1) // cols sheet = Image.new("L", (cols * w, rows * h), 255) for i, im in enumerate(imgs): x = (i % cols) * w y = (i // cols) * h sheet.paste(im, (x, y)) return sheet codes = ["B0A1", "B0A2", "B0A3", "B0A4"] export_sheet(font, codes).save("sheet.png")cols控制每行放几个字,scale小一点避免图片过大。生成对照表后,一眼就能看出哪些字缺失、哪些字点阵错位。我一般会抽 50 个常用字跑一遍,确认无误再上生产。
4. 避坑与排查:GB2312标准字库.json 最常见的 5 个坑
4.1 现象:渲染出来是乱码或空白,原因:编码大小写或前导零丢失
有些 JSON 里 code 写成b0a1,有些写成B0A1,还有的写成0xB0A1。如果你直接拿字符串去查表,大小写不一致就找不到。更隐蔽的是前导零:比如0A1其实是00A1被截断了。解决方法是统一转大写,并补足 4 位。
def normalize_code(code): c = code.upper().replace("0X", "") return c.zfill(4)查表前先 normalize,能避免大部分“找不到字”的问题。
4.2 现象:字是镜像的,原因:位序 MSB/LSB 搞反
前面提过,MSB-left 和 LSB-left 会得到左右镜像。判断方法:渲染“一”字,看非零字节的位模式。如果 bit0 对应左边,就是 LSB-left。解决就是在展开像素时改循环方向。
# LSB-left 展开 for bit in range(0, 8): pixels.append((b >> bit) & 1)改完再渲染一次,镜像消失。
4.3 现象:字上下颠倒,原因:行序从下到上
有些字库为了适配特定屏幕扫描方向,行序是倒的。表现是“二”字上面一横跑到下面。解决:在填充img时把行索引反过来。
for row in range(h): target_row = h - 1 - row img[target_row, :] = pixels[:w]如果同时有镜像和倒置,就两个都调,直到肉眼验证正确。
4.4 现象:点阵长度对不上,原因:bytesPerRow 计算错误或文件截断
16×16 点阵,如果 JSON 里 bytesPerRow 写成 1,那 bitmap 长度就是 16,但实际需要 32。加载时校验len(bitmap) == height * bytesPerRow能直接暴露。如果 JSON 没写 bytesPerRow,就按(width + 7) // 8算。注意宽度不是 8 的倍数时,比如 12×12,每行占 2 字节,但只有前 12 位有效。
4.5 现象:部分汉字缺失,原因:字库只覆盖一级汉字或区位码不连续
GB2312 一级汉字 3755 个,二级汉字 3008 个。有些精简字库只包含一级汉字,区位码 16~55 区。如果你查 56 区之后的字找不到,先确认字库覆盖范围。解决:在加载时统计 code 的区位分布,或者直接查文档。没有文档就遍历一遍,看最大区号是多少。
def coverage(font): zones = set() for code in font["codeMap"]: qu = int(code[:2], 16) - 0xA0 zones.add(qu) return min(zones), max(zones)coverage返回最小和最大区号,能快速判断字库范围。
5. 进阶:把 GB2312标准字库.json 用进嵌入式与前端渲染
5.1 生成 C 数组供单片机使用
嵌入式环境通常需要把字模转成 C 数组,直接编译进固件。思路是只提取用到的字,按需生成。
def gen_c_array(font, codes, name="font_data"): lines = [f"const unsigned char {name}[][32] = {{"] for code in codes: g = font["codeMap"][code.upper()] bytes_list = [str(b) for b in g["bitmap"]] lines.append(" {" + ",".join(bytes_list) + "},") lines.append("};") return "\n".join(lines) print(gen_c_array(font, ["B0A1", "B0A2"]))这里假设 16×16 点阵,每字 32 字节。如果字宽不同,数组第二维要改。生成后直接贴进 C 文件,配合显示驱动逐字节写屏。
5.2 前端 Canvas 直接渲染 JSON 字模
前端可以把 JSON 当静态资源加载,用 Canvas 画点阵。适合做在线字模预览工具。
async function renderGlyph(jsonUrl, code, canvas) { const res = await fetch(jsonUrl); const font = await res.json(); const g = font.glyphs.find(x => x.code.toUpperCase() === code.toUpperCase()); if (!g) return; const ctx = canvas.getContext("2d"); const w = font.width, h = font.height; const bpr = font.bytesPerRow || Math.ceil(w / 8); canvas.width = w; canvas.height = h; ctx.clearRect(0, 0, w, h); for (let row = 0; row < h; row++) { for (let col = 0; col < w; col++) { const byteIndex = row * bpr + Math.floor(col / 8); const bit = 7 - (col % 8); const val = (g.bitmap[byteIndex] >> bit) & 1; if (val) { ctx.fillRect(col, row, 1, 1); } } } }这段 JS 和 Python 逻辑一致,只是用 Canvas 的fillRect画 1×1 像素。bit = 7 - (col % 8)对应 MSB-left,如果字镜像就改成col % 8。
5.3 一个验证技巧:用“永”字检查字库完整性
“永”字包含点、横、竖、撇、捺,笔画分布广,是检验点阵字库的经典样本。我每次拿到新的 GB2312标准字库.json,都会先渲染“永”字。如果“永”字结构正常,基本可以判定位序、行序、宽高都没问题。如果“永”字缺笔画或错位,其他字也不用看了。这个习惯帮我省了很多返工时间。希望帮到你。
本文还有配套的精品资源,点击获取