「客戶清單裡的葛字,在畫面與列印表單看起來不一樣。客戶抱怨資料一定壞了。」── 業務系統維護裡,這類諮詢並不罕見。另一個常見的是「要交到公所的文件上,人名裡的某個字顯示不出來。舊 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 的字元編碼與換行符 - Shift_JIS / UTF-8 / UTF-16、亂碼、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 的字元編碼與換行符 - Shift_JIS / UTF-8 / UTF-16、亂碼、CRLF / LF,為何混亂
本文整理 Windows 上字元編碼與換行符容易混亂的核心:bytes、UTF-8 與 CP932、UTF-16LE、BOM、CRLF 與 LF 是不同軸的概念,亂碼源於以錯誤前提 decode,且誤儲存後無法還原。讀完即可在規格中寫出明確的 encoding 與換行約定,...
從睡眠恢復就壞掉的應用程式 ── 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 的危險性與 D...
多執行緒實務最佳實踐 C++ 篇 ── 以 RAII 與 jthread 從結構上消除事故
C++ 的多執行緒是資料競爭會變成未定義行為的世界。本文整理 std::thread 解構函式的陷阱、以 jthread 與 stop_token 設計停止機制、scoped_lock 的死鎖迴避、atomic 的正確定位,以及與 Win32 同步 API 的使用區分。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
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。