人名、地名里的生僻字:系统怎么做
目录
适合谁看:开发会员、订单、政务、金融等要录入人名和地址的系统,不想让生僻字用户「改名」的开发者。
常见的坑
| 环节 | 常见问题 | 正确做法 |
|---|---|---|
| 数据库 | MySQL 用 utf8(即 utf8mb3),存不下 4 字节字符,扩展区的字被截断或报错 |
库、表、字段、连接全部用 utf8mb4 |
| 输入校验 | 用 [\u4e00-\u9fa5] 判断「是不是汉字」,把扩展区的字当成非法字符 |
用 Unicode 属性判断,如 JavaScript 的 /\p{Script=Han}/u |
| 长度限制 | 按 UTF-16 单元计算长度,一个扩展区汉字算 2 个字符,名字被判「太长」 | 按码位或「字素」计算长度 |
| 截断显示 | 截断时把代理对、变体选择符切开,出现乱码 | 按码位或字素截断 |
| 旧接口 | 和外部系统用 GBK 交换数据,扩展区的字变问号 | 用 UTF-8;必须用国标编码时用 GB 18030 |
| 显示 | 字体没有这个字,显示方框 | 字体栈加补字字体,见《网页和系统里怎么显示生僻字》 |
| 打印、证件 | 打印机或 PDF 没有嵌入字体 | 生成 PDF 时嵌入有这个字的字体 |
代码示例
JavaScript 判断是否全是汉字(包括扩展区):
const isHan = (s) => /^\p{Script=Han}+$/u.test(s);
isHan("𠮷"); // true(U+20BB7,扩展 B)
按码位计算长度:
[..."𠮷野"].length; // 2,而 "𠮷野".length 是 3
MySQL 建表:
CREATE TABLE person (
name VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
);
连接时也要指定 utf8mb4,否则数据在传输中就会出错。
测试清单
上线前,至少用下面几类字测一遍:
- 扩展 A 的字(基本平面,但很多字体没有);
- 扩展 B 及以后的字(4 字节);
- 带变体选择符的字,见《异体字、变体选择符和汉字描述序列》;
- 输入、保存、搜索、导出 Excel、打印、短信、对接第三方,每个环节都要过。
合规要求
用于政务服务和公共服务的产品,GB 18030-2022 要求满足实现级别 3,见《GB 18030:中文编码的国家标准》。