Unicode 字元檢視
找出那個把你的資料弄壞的字元。
本機處理完全在你的瀏覽器裡執行 —— 打開開發者工具的 Network 分頁就能驗證
逐字檢視
| # | 字元 | 碼位 | UTF-8 | UTF-16 | 類別 | 寬度 | 區段/備註 |
|---|
還沒有內容 —— 輸入或貼上一些文字。
貼上任何東西,一個碼位一個碼位地看:U+ 值、精確的 UTF-8 位元組、UTF-16 單位、類別、東亞寬度,以及它是不是那些「畫出來什麼都沒有」的字元之一。
碼位、編碼單位、位元組、看得見的字元
碼位是 Unicode 表上的一個號碼,寫成 U+4E00。編碼單位是某種編碼裡固定大小的一塊:UTF-16 對多數字元用一個單位,U+FFFF 以上用兩個;UTF-8 對 ASCII 用一個位元組,多數拉丁重音字母與希臘字母用兩個,CJK 區用三個,增補平面用四個。而「看得見的字元」是讀者會說「這是一個字」的東西,它可能由好幾個碼位組成。
幾乎每一個讓人困惑的文字 bug,都是這四者裡的某兩個對不上:上限用一種單位訂、卻用另一種單位檢查;substring 切在代理對的中間,畫出一個破框;長度檢查過了但資料庫寫入失敗。這一頁把四個數字一次擺出來,你就看得出來是哪一對在打架。
一個中文字是三個位元組
這一個事實可以解釋一整族問題。以位元組宣告的 VARCHAR 欄位,實際裝得下的中文只有數字的三分之一。QR Code 的容量是位元組預算不是字元預算,所以中文內容填滿它的速度快三倍,需要更大的符號。檔名長度上限、HTTP 標頭長度、簡訊分段大小、JSON 大小限制,全部都是以位元組計的。
這裡的位元組欄位是真正的 UTF-8 編碼結果,不是估算,所以你可以拿某個字串去對某個具體預算。如果你要弄清楚為什麼一個欄位收得下英文姓名、卻退掉同一個人的中文姓名,答案就在這裡。
那些畫出來什麼都沒有的字元
零寬空格、零寬連接符、軟連字號、位元組順序記號、詞連接符、書寫方向標記、不斷行空格 —— 全部都是真實存在的字元:它們佔長度、參與比對,而且完全看不見。它們是從網頁、PDF、試算表與通訊軟體複製時帶進來的,也是「這兩個字串明明一樣,比對卻失敗」最常見的答案。
我們把它們用方括號連同碼位印出來,所以表格是告訴你它們「在哪裡」,而不只是有幾個。位置是有意義的:搜尋關鍵字中間夾一個零寬空格,跟檔案開頭有一個 BOM,是兩個不同的問題,而其中只有一個靠 trim 就能解決。
正規化,以及那個會悄悄改變意義的選項
要可靠地比較文字,先正規化成 NFC:它把帶重音的字母組合成單一碼位形式,而那是多數系統產生與預期的形式。NFD 是反向操作,當一個從 Mac 來的檔名怎麼都比不對時值得看一下。這兩者互轉都是無損的。
NFKC 是要小心的那一個。它會折疊相容字元 —— 全形英數變成 ASCII、帶圈數字變成數字、合字被拆開 —— 這在把使用者輸入正規化拿去搜尋時正是你要的,而在那個區別本身帶有意義時正是你不要的。對日文套 NFKC 會把半形片假名折掉;對中文文件裡刻意使用的全形英數套 NFKC,會改變整行的排版。決定之前,先比較這一頁上 NFC 與 NFKC 兩個數字。
東亞寬度,以及固定版面為什麼會爛掉
一個中日韓字元在等寬環境裡佔兩欄,全形英文字母也是。這就是為什麼用空白對齊的終端機表格一出現中文姓名就散掉、為什麼以字元數計算的行寬限制對中文會低估大約一半、以及為什麼固定寬度的文字排版很少活得過翻譯。
寬度欄位報的是 Unicode 的 East Asian Width 屬性:W 寬、F 全形、H 半形、Na 窄,以及 A —— 寬度取決於上下文的歧義類。這一頁的一切都跑在你的瀏覽器裡,而這件事很重要,因為人們拿來檢查的字串,通常正是已經在出事的那些:API 金鑰、客戶資料、log 內容與檔名。
常見問題
為什麼我的 emoji 顯示兩個 UTF-16 單位?
因為它在 U+FFFF 以上,是用代理對儲存的。把字串切在兩半之間,就會產生你有時看到的那個破框。
怎麼找出文字裡看不見的字元?
貼進來就好。隱形字元在表格裡會以方括號加名稱或碼位顯示,上面的統計也會告訴你有幾個。
比較字串之前該用哪一種正規化?
幾乎都是 NFC。只有在你確實要把全形與相容形式折在一起時才用 NFKC,並且要知道那個折疊是有損的。
你們會顯示 Unicode 官方字元名稱嗎?
不會。完整的名稱表是一個很大的下載檔,所以我們顯示類別、書寫系統、區段,以及真正會造成 bug 的那些屬性,而且欄位標題就照實寫,不會讓你以為那是官方名稱。
我貼進去的文字會被送出去嗎?
不會。全部在這個分頁裡計算。沒有任何請求夾帶你的輸入 —— 這是刻意的,因為人們在這裡檢查的是金鑰、token 與客戶資料。