「客户清单里的葛字,在画面与列印表单看起来不一样。客户抱怨数据一定坏了。」── 业务系统维护里,这类咨询并不罕见。另一个常见的是「要交到公所的文件上,人名里的某个字显示不出来。旧 PC 显示得出;换机后变成 □。」
两者现场都容易叫「乱码」,但与编码不一致造成的乱码是不同问题。前者数据一个位元都没变、只有外观变了;后者是只存在于那台 PC 的「外字」丢了。
flowchart TB
accTitle: 两种常见咨询实际上是什么
accDescr: 葛在画面与表单形状不同的咨询,是数据不变只有外观变了;换机后变成 □ 的咨询,是只存在于那台 PC 的外字丢了;两者都与编码不一致乱码是不同问题
c1["咨询 1:画面与表单形状不同"] --> r1["数据不变;只有外观变了"]
c2["咨询 2:换机后变成 □"] --> r2["只存在于那台 PC 的外字丢了"]
r1 --> diff["与编码乱码不同的问题"]
r2 --> diff
图 1: 容易被叫「乱码」的两种咨询,都与编码不一致是不同问题。
本文的承诺很单纯。只要把字符码(数据)层与字体(外观)层分开,多数日文字符麻烦就能处理。 从 JIS2004 字形变更、异体字选择器(IVS)、外字(EUDC),到政府的文字基盘、再到选字体与嵌入,整理成业务系统开发者与 IT 人员能拿来决策的形状。
Shift_JIS ↔ UTF-8 转换里发生的「乱码」本身已有既有文章,因此本文集中在「码来回正确,但外观或能否显示偏掉」的问题。
1. 先说结论
- 「乱码」与「字形不同」是不同问题。乱码是数据层把字节序列解错的事故;字形差异是外观层字体持有的字形不同的事故;对策完全不同。
- 即使 Unicode 码位相同,显示的字形仍依字体而定。JIS X 0213:2004 把葛、辻、饴等 168 字的例示字形改成印刷标准体,Windows 也从 Vista 起在 MS Gothic/MS Mincho 把 JIS2004 字形当默认。12
- 把字形当数据固定下来的标准手段是异体字选择器(IVS)。用基底字加上 U+E0100 起的选择器序列指定字形;Adobe-Japan1、Hanyo-Denshi、Moji_Joho(文字信息基盘)等收藏登记在 Unicode 的 IVD。34
- 在不支援的环境,IVS 的规格行为是忽略选择器、显示基底字的默认字形。不过带 IVS 的一个字在 UTF-16 最多可到四个码元,因此字数与切片的实作要小心。5
- 外字(EUDC)的命运是「只能在那台 PC 显示」。私人使用区码位没有约定意义,登记在 eudc.tte 的字形不会跟着走到另一台 PC、邮件或 PDF。67
- 处理人名的系统应决定受理字集并写明。政府侧以户籍统一文字与文字信息基盘为基础,符合标准的系统正朝使用「行政事务标准文字」移动。8910
- 表单与 PDF 的基准是「与画面对齐字体,并嵌入」。能不能嵌入由字体授权(fsType)决定,长期保存的 PDF/A 要求字体嵌入。1112
- 不要随便对人名数据套正规化(NFKC)。统一全形半形、替换相容字,会丢掉该留的区别。13
一句话:「你存哪一段字节序列」是数据设计问题;「看起来怎样」是字体设计问题。两件事混在一起讨论,连修得了的问题都会修不了。
2. 把数据与外观分开想 ── 码位与字形
在 Unicode,字符用称为码位的数字表示。葛是 U+845B,这个数字在每台 PC 都一样。那个数字在画面或纸上怎么画,则由字体持有的字形决定。同一 U+845B 在字体 A 与字体 B 细节不同,是正常行为。
以这两层为前提,现场症状可拆成下面这样。
| 层 | 发生的事故 | 典型症状 | 主要对策 |
|---|---|---|---|
| 数据层(字符编码) | 编码解错、转换遗失 | 𦈐ッ 这类乱码、被 ? 或 〓 取代、U+FFFD(�) |
找出并修正转换路径 |
| 外观层(字体) | 依字体的字形差异、缺字形 | 同一数据但形状不同;变成 □(豆腐) | 统一或更换字体;嵌入 |
拆分的线索,记得 「�」与「□」的差异 很有用。U+FFFD(REPLACEMENT CHARACTER)的「�」是数据层转换失败的痕迹,原字已经丢了。「□」则多半只是数据还在但字体没有字形,换字体可能就能显示。
flowchart TB
accTitle: 依 � 与 □ 拆症状
accDescr: 字显示不正确时,� 是数据层转换失败、原字已丢的痕迹;□ 只是数据还在但字体没有字形,换字体可能就能显示
symptom["字显示不正确"] --> which{"你看到什么?"}
which -->|看到 �| datalayer["数据层事故"]
datalayer -.-> lost["转换失败的痕迹(原字已丢)"]
which -->|看到 □| viewlayer["外观层事故"]
viewlayer -.-> noglyph["只是字体没有字形"]
noglyph --> fixable["换字体可能就能显示"]
图 2: � 是数据层事故的记号,□ 是外观层事故的记号,调查入口因此不同。
编码本身的基础(CP932 与 UTF-8、BOM、换行码)写在〈厘清 Windows 的文字编码 - 乱码为什么会发生,尤其是与 Linux 搭配时什么地方会偏掉〉与〈整理 Windows 的字符编码与换行符 - Shift_JIS / UTF-8 / UTF-16、乱码、CRLF / LF,为何混乱〉。从这里起是外观层,以及发生在其边界的问题。
3. 从 JIS90 到 JIS2004 ── 码没变,字形变了
开头「葛在画面与表单看起来不一样」的身分,多数情况在这里。
依国语审议会 2000 年报告「表外汉字字体表」,2004 年修订 JIS X 0213:2004(通称 JIS2004)把 168 个汉字的例示字形改成接近所谓康熙字典体的印刷标准体。葛、辻、饴、芦、溢、饼等是代表例子。1
Windows 对齐这个,从 Windows Vista 起在 MS Gothic/MS Mincho(以及新导入的 Meiryo)把 JIS2004 字形当默认。现行 MS Gothic 也是 JIS2004 系默认字形,JIS90 时代字形可经 OpenType jp90 功能存取。21
flowchart TB
accTitle: 现行 MS Gothic 的字形结构
accDescr: 从 Vista 起,MS Gothic 以 JIS2004 字形为默认,经 OpenType jp90 功能存取 JIS90 时代字形是结构
msg["MS Gothic(Vista 起)"] --> def["默认字形:JIS2004 系"]
msg --> feat["经 jp90 功能"]
feat --> old["JIS90 时代字形"]
图 3: 现行 MS Gothic 以 JIS2004 字形为默认,可用 jp90 功能切到 JIS90 字形。
这里重要的是只有字体变了;数据完全没变。
- 葛的码位在 XP 与 Windows 11 都是 U+845B
- 在 XP(JIS90 字形)显示成把包覆部首内部简化成 ヒ 的形;从 Vista 起(JIS2004 字形)显示成里面也写 人 的形
- 因此旧系统印的表单扫描影像,与新 PC 的画面显示,字形不一致。数据比对完全相符
辻的辶是一点还是两点、饴的「食」部首形状等也一样。不知道这段历史,调查容易往「迁移时数据坏了」的错方向走。被告知迁移前后字的外观不同时,先比对码位,若相符就怀疑字体字形差异── 那才是正确顺序。
flowchart TB
accTitle: 同一码位,字形依字体而异
accDescr: 葛的码位 U+845B 在 XP 与 Windows 11 都一样;只有显示形状在 JIS90 字形字体与 JIS2004 字形字体之间改变,数据比对完全相符
cp["码位 U+845B(葛)"] --> f90["JIS90 字形字体(XP)"]
cp --> f04["JIS2004 字形字体(Vista 起)"]
f90 --> g90["内部简化成 ヒ 的形"]
f04 --> g04["里面写 人 的印刷标准体"]
g90 -.-> same["数据比对完全相符"]
g04 -.-> same
图 4: 只有字体变了;码位 U+845B 在每个环境都一样。
注意因为字本身没变,两种字形都是「同一个字」。不过在人名里,当事人或公所有时坚持特定形,要把那个区别「当数据」回应,就是下一个主题 IVS。
4. 异体字选择器(IVS)── 把字形当数据指定
IVS(Ideographic Variation Sequence)是在汉字正后方放称为「异体字选择器」的不可见码位,把字形变体当数据指定的机制。使用的选择器是 U+E0100–U+E01EF(VS17–VS256)。3
哪个「基底字 + 选择器」序列指哪个字形,由 Unicode Consortium 管理、称为 IVD(Ideographic Variation Database) 的注册库决定。主要收藏如下。4
| 收藏 | 登录 | 由来与用途 |
|---|---|---|
| Adobe-Japan1 | 2007 | Adobe 的日文字收藏。商业字体切换异体字形的基础 |
| Hanyo-Denshi | 2010 | 泛用电子信息交换环境整备计划。对应户籍与住民基本台帐等政府文字 |
| Moji_Joho | 2014 | 对应文字信息基盘(MJ)。与 IPAmj 明朝一起用。2026 年 8 月也有追加登记 |
Microsoft 文件例如写 U+845B 单独(葛)用于西葛西站的写法,U+845B+U+E0100(VS17)用于奈良县葛城市的写法。同一个葛,哪个字形可以当数据区分。3
flowchart TB
accTitle: 用 IVS 把同一个葛当数据区分的例子
accDescr: 单独 U+845B 的葛用于西葛西站的写法;U+845B 加上 VS17 的序列用于葛城市的写法;哪个序列指哪个字形由 IVD 注册库决定
seq1["单独 U+845B"] --> gl1["西葛西站写法使用的字形"]
seq2["U+845B + VS17"] --> gl2["葛城市写法使用的字形"]
ivd["IVD(注册库)"] -.-> gl1
ivd -.-> gl2
图 5: 即使同一个葛,有没有选择器也能把哪个字形当数据区分。
4.1. 在不支援的环境里的行为
字体侧,IVS 与字形的对应实作在 OpenType cmap 表(format 14)。5 支援字体(IPAmj 明朝等)与支援应用都在时,指定字形会出现;不在时如下。
- 规格上正确的行为:忽略选择器,显示基底字的默认字形(选择器本身不可见)
- 较旧应用与部分绘制调用栈:把选择器当成独立的未知字,多显示一个 □
也就是说 IVS 设计成「即使降级,基底字仍可读」,但「一定会以指定字形显示」的保证取决于接收端环境。政府的住民纪录与户籍系统使用文字信息基盘字体加上 IVS 的组合,但一般业务系统随便接受,字形会在显示、列印或下游系统某处掉下去。
flowchart TB
accTitle: 带 IVS 的数据怎么显示
accDescr: 支援字体与支援应用都在时以指定字形显示;不在时忽略选择器、显示基底字默认字形;较旧应用与部分绘制调用栈把选择器当成未知字、多显示一个 □
ivs["基底 + IVS 选择器"] --> env{"支援字体 + 应用?"}
env -->|是| ok["指定字形"]
env -->|否| other{"怎么画?"}
other -->|忽略| ignore["默认字形"]
other -->|较旧/部分调用栈| tofu["多一个 □"]
ignore -.-> spec["规格上正确"]
图 6: IVS 即使降级仍可读成基底字,但指定字形会不会出现取决于接收端环境。
4.2. 实作注意 ── 「一个字」最多可到四个码元
U+E0100 起的 IVS 选择器是补充平面上的码位,因此在 UTF-16 一律是代理对(两个码元)。若基底字是补充平面汉字(例如 JIS2004 追加的 𠮟(U+20B9F)),光基底就已经两个码元,用户认作「一个字」的序列在 UTF-16 最多四个码元,在 UTF-8 最多八字节。
- C# 的
"葛󠄀"(葛+VS17)的string.Length == 3。Substring与固定长度切片有把基底字与选择器切开的风险 - 字数验证与切片应以字素为单位(
StringInfo等 API),不是码元 - 对 DB 栏长(SQL Server 的
nvarchar(n)以 UTF-16 码元计),若接受 IVS,预留外观字数的两到四倍 - 在搜寻与比对,有没有选择器就是不同字串。「葛」的搜寻会不会打中「葛+VS17」,需要当需求决定并实作
flowchart TB
accTitle: 带 IVS 的一个字与 UTF-16 码元
accDescr: 用户认作一个字的基底字与异体字选择器序列,选择器一律是代理对,若基底字是补充平面汉字再加两个码元,UTF-16 最多四个码元
one["一个看得见的字"] --> base["基底字"]
one --> vs["变异选择器"]
base -.-> bnote["补充平面则 +2"]
vs -.-> vnote["一律 2 个码元"]
base --> total["最多 4 个 UTF-16 单位"]
vs --> total
total -.-> risk["固定切片会切开"]
图 7: 带 IVS 的一个字在 UTF-16 最多可到四个码元;依码元切片很危险。
5. 外字(EUDC)── 只在那台 PC 显示的字
外字是用户把自己的字形指派到 Unicode 私人使用区(PUA:U+E000–U+F8FF 等)码位的机制。私人使用区码位没有全世界约定的意义;同一 U+E000 可依 PC、依组织指派不同字。6
在 Windows 用私人字符编辑器(eudcedit.exe)做字形,存在称为 eudc.tte 的字体档。此档以隐藏字体安装,并在 HKEY_CURRENT_USER\EUDC 登录与各字体关联。7 Shift_JIS(CP932)时代外字范围是 0xF040–0xF9FC,转成 Unicode 时对应到私人使用区。
这个机制的后果很清楚。
- eudc.tte 属于那台 PC(那位用户),不会跟着数据走到对方
- 传到邮件、PDF、Web、另一套系统的那一刻,就变成 □ 或看起来像对方不同的外字
- OS 迁移或换机时忘了搬 eudc.tte,「旧 PC 显示得出的字显示不出」就发生
这就是开头第二个咨询的身分。
flowchart TB
accTitle: 为什么外字只在那台 PC 显示
accDescr: 在私人字符编辑器做的字形存在 eudc.tte,并在那台 PC 的登录与字体关联,因此若只把私人使用区码传到邮件、PDF、另一套系统,就变成 □ 或看起来像别的字
edit["做 PUA 字形"] --> tte["存在 eudc.tte"]
edit -.-> editN["私人字符编辑器"]
tte --> reg["登录字体对应"]
reg --> local["在那台 PC 显示"]
tte -.-> stay["eudc.tte 留在后面"]
send["只有 PUA 码走"] --> dest["邮件/PDF/其他系统"]
dest --> broken["□ 或错字"]
local ~~~ send
图 8: 字形住在 eudc.tte;数据里只剩私人使用区号码,因此外字一离开 PC 就看起来坏掉。
5.1. 已经收进外字的系统的现实答案
问题是从旧系统继承的数据已经混进外字。迁移委托上我们建议的程序如下。
- 调查:用正规表示式扫描数据库与文件的私人使用区(U+E000–U+F8FF),盘点使用中的外字码与次数。从各据点 PC 收集 eudc.tte 并确认字形
- 识别:对外字逐一调查「能否表示成正规 Unicode 字」「能否用 IVS 表示」「文字信息基盘(MJ)有没有对应字」,做替代字对应表。实务上多数情况只是旧形曾被做成 JIS 外字
- 替换:依对应表替换数据。只有真正没有对应字时,以影像保留或在该笔记录附注
- 切断:在新系统用验证拒绝私人使用区输入,不要再做新外字
flowchart TB
accTitle: 迁移含外字数据的程序
accDescr: 扫描私人使用区并收集 eudc.tte 来盘点使用中的外字,做替代字对应表并替换,在新系统用验证拒绝私人使用区输入、不要再做新外字
st1["调查:扫描 PUA"] --> st2["识别:替代表"]
st2 --> st3["依表替换"]
st3 --> st4["切断:不要新外字"]
st1 -.-> tte["收集 eudc.tte"]
st2 -.-> nomap["没有对应:影像或附注"]
图 9: 以外字依调查、识别、替换、切断四阶段迁移,不要再做新外字。
政府侧方向相同:已表明把自治体自行做的外字(据说全国约两百万字)对行政事务标准文字唯一识别、并停止使用的方针。10「不要增加外字;对标准化字集识别」正在成为公私部门都确立的迁移模式。
6. 政府文字基盘 ── 从户籍统一文字到行政事务标准文字
在设计处理人名的系统时,知道政府侧文字基盘,成为决定「收到多广」的材料。
| 名称 | 主管 | 概要 |
|---|---|---|
| 户籍统一文字 | 法务省 | 为户籍电脑化整理的约 5.6 万字。可在法务省网站搜寻8 |
| 住基网统一文字 | J-LIS(地方公共团体信息系统机构) | 住民基本台帐网络上使用的约 2.1 万字 |
| 文字信息基盘(MJ) | 文字信息技术促进协议会 | 整理行政事务使用的约 6 万字。以 MJ 字形名称管理;公开 IPAmj 明朝字体与 MJ 文字信息一览。以 IPA 专案整理,现已移交给协议会9 |
| 行政事务标准文字(MJ+) | 数字厅 | 把文字信息基盘加上无法对 MJ 识别的户籍文字等延伸的字集。符合标准的系统里人名等使用此字集;字符编码是 JIS X 0221:202010 |
在自治体核心业务系统(符合标准的系统),标准规格是两层结构:人名等信息互通使用行政事务标准文字,与没有统一互通规则的外部系统──智能手机等──以 JIS X 0213:2012 范围互通。10「内部持有宽字集,与外部用一般环境能显示的范围交换」这个结构本身,也是民间系统的参考。
flowchart TB
accTitle: 符合标准系统的两层互通
accDescr: 自治体符合标准系统对人名等信息互通使用行政事务标准文字,与没有统一互通规则的智能手机等外部系统以 JIS X 0213:2012 范围互通
sys["自治体标准系统"] --> renkei["人名互通"]
sys --> gaibu["外部系统"]
renkei --> mjp["行政标准文字"]
mjp -.-> mjpN["人名等"]
gaibu --> jis["JIS X 0213:2012 范围"]
gaibu -.-> sumaho["没有规则(智能手机)"]
mjp -.-> naibu["内部持有宽字集"]
图 10: 两层结构:政府互通用行政事务标准文字;没有规则的外部互通用 JIS X 0213:2012。
对一般业务系统的实务指引,我们建议如下。
- 决定受理字集,并写进规格与输入验证两边。例如「JIS X 0213:2012 范围」「不允许私人使用区与结合字」「不接受 IVS(或接受,但只在 IPAmj 明朝环境保证显示)」
- 不要无上限接受。「是 Unicode,所以什么都行」的设计,会在显示、列印或互通某处坏掉
- 事先决定范围外字的营运。用替代表示(新形、片假名)替换的规则,以及对当事人说明的文案,本身就是系统规格
- 当政府或金融等下游系统有字集规则时,以那个为准并对齐
flowchart TB
accTitle: 设计并营运受理字集
accDescr: 决定受理字集并写进规格与输入验证两边;接受范围内的字;对范围外的字,连替代表示规则与对当事人说明的文案都决定营运
decide["决定受理字集"] --> spec["写进规格"]
decide --> valid["写进输入验证"]
valid --> range{"在范围内?"}
range -->|是| ok["接受"]
range -->|否| alt["用替代表示替换"]
alt -.-> word["对当事人说明的文案也是规格"]
图 11: 把受理字集写进规格与输入验证两边,范围外营运也决定好。
7. 选字体与嵌入 ── 对齐画面与表单
7.1. 常见字体的性格
| 字体 | 涵盖 | 性格与用在哪 |
|---|---|---|
| MS Gothic/MS Mincho | Windows 标准 | 为低解析度画面设计的老手。默认字形是 JIS2004 系2。仍在为维持与旧表单相容而服役 |
| Meiryo | Vista 起 | 假设 ClearType 的现代画面字体。与 Vista 世代 JIS2004 迁移同时出现1 |
| Yu Gothic/Yu Mincho | Windows 8.1 起 | Windows 与 macOS 都出货的家族,较容易对齐文件外观 |
| BIZ UD Gothic/BIZ UD Mincho | Windows 10 1809 起 | 森泽通用设计字体。强调表单与画面可读性的委托上是第一候选14 |
| Noto Sans JP | 另外安装 | 以开放源代码提供,容易在服务器或 Linux 环境捆进,也容易在 Web 交付 |
选择上重要的,与其说是字体偏好,不如说是该字体是否存在于显示、列印、PDF 产生涉及的每个环境。Windows 10/11 的日文补充字体(BIZ UD 等)有时依配置不在,在服务器侧产生 PDF 的配置里,服务器上有没有字体有直接影响。
flowchart TB
accTitle: 选字体时要确认的环境
accDescr: 选字体时,重要的与其说是字体偏好,不如说是该字体是否存在于显示、列印、PDF 产生涉及的每个环境;补充字体的配置与服务器上有没有字体有直接影响
cand["候选字体"] --> exist["每个环境都有?"]
exist --> scr["显示环境"]
exist --> more{"列印或 PDF 服务器?"}
more --> prn["列印环境"]
more --> srv["PDF 产生服务器"]
scr -.-> hojo["补充字体?"]
hojo -.-> hojoN["可能不在"]
srv -.-> eikyo["服务器上的字体很重要"]
图 12: 选字体与其依字体偏好,不如依它是否存在于显示、列印、PDF 产生的每个环境。
7.2. 表单设计的基础 ── 对齐,并嵌入
- 画面与表单指定同一字体。字体不同,同一份数据可能看起来像不同字形,就会接到开头那种抱怨。「画面 Meiryo、表单 MS Mincho」这类配置,至少应检查 168 个 JIS2004 字有没有字形差异
- 把字体嵌进 PDF。不嵌入,检视端会用手边字体替代绘制,不只字形、版面也可能变
- 能不能嵌入由授权决定。OpenType 字体在
fsType栏宣告嵌入权限(Installable/Restricted/Preview & Print/Editable、禁止子集等),不得嵌入未授权嵌入的字体。11 商业字体必须确认契约 - 以子集嵌入为基准。只嵌入用到的字的字形,就不必扛整套日文字体(数 MB 到数十 MB)
- 若有长期保存要求,PDF/A。PDF/A(ISO 19005)是把显示所需资源收进档内的标准,要求字体嵌入。12 也是防止「十年后打开字形变了」最可靠的做法
flowchart TB
accTitle: 字体嵌入的决策流程
accDescr: 把字体嵌进 PDF 之前,确认 fsType 嵌入授权;若已授权,以子集嵌入为基准;若有长期保存要求,考虑要求嵌入的 PDF/A
emb["把字体嵌进 PDF"] --> lic{"fsType 授权嵌入?"}
lic -->|已授权| sub["以子集嵌入为基准"]
lic -->|未授权| ng["不得嵌入"]
sub -.-> gly["只用到的字的字形"]
sub -->|长期保存要求| pdfa["考虑 PDF/A"]
pdfa -.-> must["要求字体嵌入"]
图 13: 嵌入以确认 fsType 授权为前提;子集嵌入与 PDF/A 是基准。
列印与 PDF 输出的实作手段怎么选,深度写在〈Windows 业务应用的列印与 PDF 输出〉。
8. 字体链接与后备 ── 「混进不同字体」的现象
指定字体没有字形的字,不会显示成什么都没有;用另一个字体替代绘制是现代绘制调用栈的默认行为。在 GDI,登录里定义的「字体链接」(FontLink\SystemLink)做这件事;在 DirectWrite、WPF、浏览器,「字体后备」做。15
flowchart TB
accTitle: 字体链接与后备的流程
accDescr: 指定字体有字形就原样显示;没有就用字体链接或后备字体替代绘制;哪里都没有字形就变成 □,但数据往往还活着
disp["显示一个字"] --> has{"指定字体有字形?"}
has -->|是| draw["用指定字体显示"]
has -->|否| fb{"链接或后备对象里有吗?"}
fb -->|是| alt["用另一个字体替代绘制"]
alt -.-> mixed["混字体感的原因"]
fb -->|否| tofu["显示 □(豆腐)"]
tofu -.-> alive["数据往往还活着"]
图 14: □ 是后备失败的痕迹;替代绘制成不成功,是「混」与「豆腐」的分叉。
知道这个机制,就能说明下列常见情况。
- 拉丁与日文的字体感不同:因为先指定了拉丁字体,只有日文部分用链接或后备的日文字体画
- 日文句子里只有汉字变成中文风格字形:后备对象解析到了中文字体。在没有正确传递语言信息(lang 属性或地区设置)的网页或应用很容易发生
- 出现豆腐(□):指定字体与后备对象都没有字形。也就是说 □ 是「后备失败的痕迹」,数据往往还活着
后备是救济机制;它不能替代从一开始就选对字体。15 在业务应用,健康的位置是「在主要显示与列印路径,单靠设计的字体就完成;后备是未预期字的保险」。多语言 UI 的字体选择思路,也见〈WinForms/WPF 应用的多语言化〉。
9. 业务应用的实作检查清单
最后,从输入到互通各层要确认的点整理成表。
| 层 | 典型事故 | 设计与实作要点 |
|---|---|---|
| 输入 | 依环境的字、带 IVS 的字、私人使用区字从 IME 进来 | 决定受理字集并验证。范围外用指引(提供替代表示)而不是错误,柜台作业才走得动 |
| 正规化 | NFKC 把 ㈱ 变成 (株)、统一全形半形、① 变成 1 这类非预期转换。即使 NFC 也会把 CJK 相容表意文字(如 U+FA19 神)换成统合表意文字 U+795E | 不要对人名与地址套 NFKC。把正规化限于用途(产生搜寻键等),原文照输入存13 |
| 保存 | 代理对与 IVS 造成栏长不足;依码元截断 | 用 UTF-8/UTF-16 存,栏长以码元留余裕。以字素为单位切片 |
| 显示 | 字体没有字形而 □;经后备字形改变 | 明确指定能显示目标字集的字体,并确认目标 OS 上的标准涵盖 |
| 列印与 PDF | 画面与表单字形差异;检视端替代绘制 | 对齐画面与表单的字体,确认授权后在 PDF 子集嵌入11 |
| 与另一套系统互通 | JIS X 0213 追加汉字、IVS、外字在 Shift_JIS(CP932)转换变成 ? 或 〓 |
在互通规格写明字符编码与字集。若还有 CP932 互通,实作检测无法转换的字与替换规则 |
正规化尤其是陷阱,正好是本文主题本身:出于「善意」套上、却压碎异体字与全形半形区别的处理。原文原样;处理做在复本上 是原则。CSV 互通的字符编码事故,深度写在〈CSV不是「纯文字」而已〉。
flowchart TB
accTitle: 原文原样;处理做在复本上
accDescr: 把输入字串原样存成原文;对复本套限于用途(如产生搜寻键)的正规化;对原文套 NFKC 会丢掉异体字与全形半形的区别
input["输入的字串"] --> orig["原文:照输入存"]
input --> copy["复本:正规化,限于用途"]
copy -.-> use["产生搜寻键等"]
orig -.-> ng["对原文套 NFKC 压碎区别"]
图 15: 把正规化限于用途并套在复本上;原文照输入存。
10. 总结
- 字符麻烦先拆成「数据层(字符编码)」与「外观层(字体)」。� 是数据层事故的记号,□ 是外观层事故的记号。
- JIS X 0213:2004 改了 168 字的例示字形,Windows 从 Vista 起以 JIS2004 字形为默认。葛、辻、饴依环境看起来不同,是字体的历史,不是数据损坏。
- 把字形当数据固定下来的标准手段是 IVS,但没有支援字体与支援应用就会落到默认字形。别忘了一个字最多可到四个 UTF-16 码元的实作影响。
- 外字(EUDC)是那台 PC 特有的资产,不能跟着数据走。现实答案是迁移时盘点、依对应表替换成正规字或 IVS,并停止做新的。
- 处理人名的系统决定受理字集并写明。政府正以户籍统一文字与文字信息基盘为基础,朝行政事务标准文字标准化,互通的系统需要跟随那个动向。
- 表单与 PDF 的基准是「与画面对齐字体、确认授权、并嵌入」。长期保存考虑 PDF/A。
- NFKC 正规化、依码元切片、CP932 转换,是默默弄坏异体字与外字的三点。以存原文、以字素为单位处理为原则。
下次被告知「字不一样」,先这样重述问题。码位相同,还是不同? 相同就是字体问题;不同就是数据问题。那一步让你不会走错调查入口。
flowchart TB
accTitle: 决定调查入口的第一个问题
accDescr: 被告知字不一样时,先比对码位相同还是不同;相同就当字体问题开始调查,不同就当数据问题
said["被告知字不一样"] --> cmp{"码位相同吗?"}
cmp -->|相同| fontp["字体问题"]
cmp -->|不同| datap["数据问题"]
图 16: 码位相同,就当字体问题开始调查;不同,就当数据问题。
相关文章
- 理清 Windows 的文字编码 - 乱码为什么会发生,尤其是和 Linux 搭配时容易出问题的地方
- Windows 的文字编码与换行符 - 乱码与 CRLF/LF 的基础
- Windows 业务应用程序的打印与 PDF 输出 ── System.Drawing.Printing / WPF / 报表库的取舍选型
- WinForms/WPF应用的多语言化 ── resx、附属程序集与区域文化切换的实务
- CSV 不是「纯文本」──C# 业务应用中 CSV 实务(字符编码・Excel 兼容性・注入攻击防范)
- Windows 应用无障碍入门 ── 为 UI Automation 与合理便利做准备
相关咨询领域
小村软件有限公司承接业务系统里围绕字符的设计与调查。从隔离「画面与表单字不同」或「迁移后人名变成 □」这类症状的原因,到从旧系统迁移时盘点外字并做替代字表、设计处理人名系统的受理字集、审查表单与 PDF 的字体嵌入配置,我们涵盖码层与字体层两边。
参考链接
-
Morisawa Inc., JIS X 0213:2004 (JIS2004) | Font glossary. 关于 JIS X 0213:2004 依表外汉字字体表把 168 个汉字的例示字形改成印刷标准体(所谓康熙字典体);以及 Windows Vista 标准内含支援 JIS2004 的字体。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MS Gothic font family. 关于 MS Gothic 家族的默认字形是 JIS2004 系,以及可经 OpenType ‘jp90’ 功能存取 JIS90 旧字形。 ↩ ↩2 ↩3
-
Microsoft Learn, The Unicode standard. 关于变异序列由基底字加上异体字选择器(VS1–VS256,U+FE00–U+FE0F 与 U+E0100–U+E01EF)组成;关于区分 U+845B 葛与 U+845B+U+E0100(VS17)(西葛西站与葛城市)的例子;以及显示需要支援字体。 ↩ ↩2 ↩3
-
Unicode Consortium, Ideographic Variation Database. 以 UTS #37 为基础的 IVS 注册库。关于 Adobe-Japan1(2007)、Hanyo-Denshi(2010)、Moji_Joho(2014)等收藏已登录,以及 2026 年 8 月版也对 Moji_Joho 收藏做了追加登记。 ↩ ↩2
-
Microsoft Learn, cmap — Character to Glyph Index Mapping Table (OpenType spec). 关于 OpenType 字体在 cmap 子表 format 14 实作 Unicode Variation Sequences;关于默认与非默认 UVS 的区分;以及支援 JIS2004 的字体中的使用例子。 ↩ ↩2
-
Microsoft Learn, End-User-Defined and Private Use Area Characters. 关于外字(EUDC)与私人使用区(PUA)字由用户或组织独立定义,以及同一码位可依电脑有不同指派──并可能碰撞。 ↩ ↩2
-
Microsoft Learn, Character Sets and Fonts. 关于 PUA(U+E000–U+F8FF 等)用于 Unicode EUDC 用途;关于在私人字符编辑器做字形;以及 EUDC 字体以 .tte 档隐藏安装,并在 HKEY_CURRENT_USER\EUDC 登录与字体关联。 ↩ ↩2
-
Ministry of Justice, Koseki Unified Character Information — search-condition input. 法务省提供的户籍统一文字官方搜寻站。关于能搜寻户籍使用文字的字形、读音、相关信息。 ↩ ↩2
-
Character Information Technology Promotion Council, Character Information Platform project. 关于文字信息基盘(MJ 字形、MJ 文字信息一览、IPAmj 明朝字体),由 IPA 在经济产业省等支援下整理、涵盖行政事务使用的约 6 万汉字,现已移交给协议会并公开。 ↩ ↩2
-
Digital Agency, Report of the Study Group on the Operation of Character Requirements in Local-Government Information Systems (July 2024). 关于自治体使用的外字据说约两百万字;关于「行政事务标准文字」(通称 MJ+)是文字信息基盘的延伸、符合标准系统里人名等的字集,字符编码是 JIS X 0221:2020;关于人名等信息互通使用行政事务标准文字,与智能手机等互通使用 JIS X 0213:2012;以及把既有外字对行政事务标准文字唯一识别、不再使用的方针。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, OS/2 — OS/2 and Windows Metrics (OpenType spec). 关于字体的 fsType 栏定义嵌入授权(Installable/Restricted License/Preview & Print/Editable、禁止子集位元等),以及应用不得嵌入未授权嵌入的字体。 ↩ ↩2 ↩3
-
PDF Association, PDF/A Basics. 关于长期保存 PDF/A(ISO 19005)要求把显示文件所需元素收进档内,字体嵌入是代表性的必要例子。 ↩ ↩2
-
Microsoft Learn, Using Unicode Normalization to Represent Strings. 关于四种 Unicode 正规化形式 NFC/NFD/NFKC/NFKD;以及 KC/KD 形式会统一全形半形等相容字并丢掉信息,因此一般不适合作为字串的正规保存形式。 ↩ ↩2
-
Microsoft Learn, BIZ UDGothic font family. 关于森泽通用设计字体 BIZ UD Gothic 从 Windows 10 版本 1809 起作为日文补充字体内含。 ↩
-
Microsoft Learn, Fonts (Globalization documentation). 关于字体后备的机制;关于 GDI 字体链接(FontLink\SystemLink 登录);关于默认字形(豆腐)的意义;以及字体链接不能替代选对字体。 ↩ ↩2
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
从睡眠恢复后损坏的应用 ── Windows 电源事件机制与扛得住恢复的业务应用
打开笔记本后业务应用的连接全断了——原因是设计从未考虑睡眠。本文根据一手资料整理 WM_POWERBROADCAST 通知流程、Modern Standby 行为、断开/重连设计、睡眠抑制与调查命令。
Windows 应用无障碍入门 ── 为 UI Automation 与合理便利做准备
在 2024 年 4 月施行的日本残障者歧视消除法修正背景下,本文以屏幕阅读器读取 Windows 应用的机制 UI Automation 为中心,从实务整理 WinForms/WPF 的命名、键盘操作、对比与验证工具。
多线程实务最佳实践 C 语言篇 ── 以 Win32 API 的方式安全编写
C 语言 × Win32 的多线程有其定式:用 _beginthreadex 创建线程、SRW 锁与条件变量、Interlocked、以停止事件 + WaitForMultipleObjects 设计停止流程。本文还将梳理 TerminateThread 的危险性与 Dll...
多线程实务最佳实践 C++ 篇 ── 用 RAII 和 jthread 从结构上消除事故
C++ 的多线程是数据竞争会变成未定义行为的世界。本文梳理 std::thread 析构函数的陷阱、jthread 与 stop_token 的停止设计、scoped_lock 的死锁规避、atomic 的正确定位,直至与 Win32 同步 API 的使用区分。
多线程实务最佳实践 .NET 篇 ── 在增加线程之前应先确定的事
针对 .NET/C# 整理「立了线程之后,偶尔崩溃・卡死」的防范设计准则。内容涵盖不自行创建线程而改用 Task、减少共享可变状态、锁的纪律、基于 CancellationToken 的停止设计,直至 UI 线程的处理方式。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 为什么同一个葛字会依 PC 或列印表单看起来不一样?
- 比较像是字体字形的差异,而不是乱码。JIS X 0213:2004(JIS2004)把 168 个汉字的例示字形改成印刷标准体,Windows 也从 Vista 起在 MS Gothic/MS Mincho 等把 JIS2004 字形当默认。葛、辻、饴等是代表例子:Unicode 码位(数据)不变,只有字体持有的字形(外观)变了。比对数据会相符;XP 时代表单影像与新 PC 画面形状不一致,是规格行为。若也要把字形对齐,画面与表单用同一字体,或用异体字选择器指定字形。
- 用了异体字选择器(IVS),是不是所有人名字形问题都解决?
- 不会。IVS 是在基底字正后方放 U+E0100 起的选择器、把字形当数据指定的机制,只有支援字体(如 IPAmj 明朝)与支援应用都在时,指定字形才会显示。在不支援的环境,规格行为是忽略选择器、显示基底字的默认字形;有些环境选择器也会变成 □。再者,带 IVS 的一个字在 UTF-16 最多可到四个码元,会影响字数、切片、以及 DB 栏长设计。导入时,要先确认显示、列印、下游系统的支援范围再使用。
- 登记成外字(EUDC)的字,能在另一台 PC 或 PDF 显示吗?
- 原则上不能。外字是用户把字形登录到该 PC 的 eudc.tte,码位在 Unicode 私人使用区(U+E000 起);同一码位在另一台 PC 未定义或是别的字形。因此传到邮件、PDF、另一套系统的命运是变成 □ 或看起来像别的字。若已经继承含外字的数据,迁移时的现实路径是盘点私人使用区的使用、定位到正规 Unicode 字或异体字选择器的对应表、再替换。新系统应避免再做新外字。
- 业务系统该把人名里的字收到多广?
- 第一件事是「决定受理字集并写成规格」。户籍大约有 5.6 万个户籍统一文字,符合政府标准的系统正朝使用行政事务标准文字(文字信息基盘的延伸)移动──但一般业务系统没有义务无上限收同一水准。现实设计是决定「到 JIS X 0213 范围」或「不接受异体字选择器或私人使用区」这类范围,输入时验证,范围外用警示或替代表示来营运。只有与政府系统或自治体互通的系统,才需要跟随行政事务标准文字与以 JIS X 0221 为基础的互通要求。
- 要怎么让表单或 PDF 显示与画面相同的字?
- 基准是画面与表单指定同一字体,并把字体嵌进 PDF。字体不同,同一份数据可能改字形;检视端 PC 没有该字体,就会用替代字体绘制、外观坏掉。能不能嵌入由字体授权(OpenType fsType)决定,因此不要丢给报表函数库,自己确认。只嵌入用到的字的子集嵌入,也能压低文件大小。若有长期保存要求,考虑要求字体嵌入的 PDF/A。