Unicode 純量值是介於 U+0000 到 U+10FFFF 之間的整數(不含 U+D800–U+DFFF 代理區間),用來為 Unicode 標準中的單一字元命名,而字元碼查詢則是為任何可貼上或輸入的字串取得這些數字的工作流程。命令列查詢通常仰賴諸如 `man ascii`、`python3 -c "print(hex(ord(...)))"`、Perl 單行指令、搭配 `%x` 的 `printf`、`hexdump`、`xxd`,或是每行輸出一個數字代碼的 shell 管線等工具。在本文的脈絡下,所謂線上查詢指的是像 Unicode 編碼/解碼器 這類瀏覽器工具,它會將文字轉換成以空格分隔的 U+ 符記序列,且不會切開輔助平面字元,整個過程都在你的機器上執行。這兩條路徑回答的是同一個診斷問題——這個字串實際上包含哪些抽象字元——但它們在可攜性與完整 Unicode 涵蓋範圍之間做出了相反方向的取捨:CLI 管線貼近 ASCII 與 Linux 使用者空間,而瀏覽器純量轉換器則能在不離開分頁的情況下,對表情符號、帶腔調字母、CJK,以及像 👩‍💻 這類合併序列得出相同結論。

char code lookup command line vs online
字元碼查詢:命令列 vs 線上

查詢字元碼的命令列方法

Linux 與 macOS 內建了數個兼具 ASCII 或 Unicode 對照表功能的工具。最簡單的是 `man ascii`,它會將可列印的 7 位元範圍(0–127)以十進位、八進位與十六進位並排印出。它快速、離線,且幾乎無處不預裝,但只涵蓋到基本拉丁區塊。

若要做臨時查詢,可以仰賴任何已安裝的腳本語言。在 Python 3 中,`python3 -c "print(hex(ord('é')))"` 會印出 `0xe9`,而像 `python3 -c "print(', '.join(f'U+{ord(c):04X}' for c in 'café'))"` 這類單行指令,則會為字串中每個純量產生一個乾淨的 U+ 串流。Perl 用 `perl -CSDA -e 'printf "U+%04X\n", ord for split //, "café"'` 也能達到相同效果。這兩個指令之所以有效,是因為 Python 的 `ord()` 與 Perl 的 `ord` 回傳的是 Unicode 純量值,而非 UTF-8 位元組。

`printf` 搭配 `%d` 或 `%x` 以及前導引號技巧(`printf "%d " "'A"`)會印出當前 locale 中第一個位元組的值。一旦字元需要超過一個 UTF-8 位元組,這個方法就會失效,這就是為什麼 shell 管線對 "A" 看起來正常,但在 C.UTF-8 shell 中處理 "é" 時行為卻很怪異。`xxd` 與 `hexdump -C` 是位元組層級的工具;它們會顯示原始的 UTF-8 序列(é 為 C3 A9),但不會顯示 Unicode 碼點。將位元組與碼點混為一談,是人們從 ASCII 轉移到混合文字時最常犯的錯誤。一個名為 `uni` 的獨立工具將 UnicodeData.txt 包裝成一個小型 CLI,可為任何碼點印出名稱與屬性,而部分發行版則提供 `uniname` 提供類似的查詢。這些工具對交叉比對名稱很有幫助,但需要另外安裝。

實務上的結論是:命令列透過 `man ascii` 涵蓋 ASCII 查詢,透過 Python 或 Perl 單行指令涵蓋純量查詢,透過 `xxd` 涵蓋位元組查詢,而 Unicode 名稱查詢通常需要另一個工具。針對同一工作的 UTF-8 位元組面向,一個方便的搭配工具是 Linux 上的 Base64 解碼 指南中所述的工作流程,該指南從不同角度涵蓋了相同的 shell 驅動編碼領域。

瀏覽器式的字元碼查詢

像 Unicode 編碼/解碼器這類瀏覽器工具完全在頁面上執行;不會上傳任何東西,每次轉換都留在本地。它以 Unicode 純量值而非 JavaScript UTF-16 碼元來迭代字串,因此像 😀 這類輔助平面字元會保持為單一符記(U+1F600),而不會被切成一對代理對。解碼模式接受編碼器所產生的相同標籤:以以開頭或以開頭或 JavaScript 風格的 `\u{...}` 大括號標記法,以空格、逗號或換行區隔。十六進位不區分大小寫,每個符記在轉回文字之前都會對照完整的純量範圍進行檢查。

這在診斷工作中很重要,因為問題在於字元的身份,而非位元組表示方式。將 "café" 貼入編碼器會回傳 U+0063、U+0061、U+0066、U+00E9,而將同樣的標籤貼回解碼模式則會精確重建原始字串。貼上 "👩‍💻" 會回傳三個符記——U+1F469、U+200D、U+1F4BB——因為那個表情符號是一個序列,而非單一碼點。此工具也會顯示不可見字元,例如零寬接合符(U+200D)與換行字元(U+000A),這通常能解釋為何貼上的文字無法精確比對,或為何游標會意外跳動。其實作方式是以 Unicode 純量進行迭代,並將每個純量格式化為大寫 U+ 十六進位,至少四位數,接著驗證每個已解碼的符記都落在 U+0000 到 U+10FFFF 之內,且不在代理區間內。

在瀏覽器中查詢 Unicode 純量值

  1. 開啟 Unicode 編碼/解碼器頁面,並確認選定的方向是「文字轉碼點」。
  2. 貼上你想檢查的精確字串,包含任何不可見字元,例如零寬接合符或不中斷空格。
  3. 執行轉換並讀取 U+ 符記。基本字元至少會以四位十六進位數顯示(A 會變成 U+0041),而輔助字元則保留其完整數值(😀 會變成 U+1F600,而非兩個代理半字)。
  4. 若要反向操作,請切換到解碼模式,並輸入帶前置詞的符記,例如 U+0041 或 `\u{1F600}`,以空格、逗號或換行區隔。
  5. 輸出文字只有在每個符記皆通過純量值驗證後才會產生;落在 U+0000 到 U+10FFFF 之外的符記,以及 U+D800 到 U+DFFF 代理區間中的任何數值,都會被拒絕,而非被靜默替換。

命令列 vs 線上工具一覽覽表

沒有任何一條路徑是絕對較好的。選擇取決於周邊的工作流程,以及查詢必須超出 ASCII 多遠。

使用情境命令列Unicode 編碼/解碼器(瀏覽器)
可見字 ASCII 參考`man ascii` 一頁涵蓋 32–126貼上任何字元,即可取得其 U+ 標籤
單一字元純量`python3 -c "print(hex(ord(c)))"`貼上一次,輸出一個符記
輔助平面表情符號需要 Python 或 Perl 單行指令原生支援,每個表情符號一個符記
合併/ZWJ 表情符號需在程式碼中手動切分會以明確序列呈現
UTF-8 位元組檢視`xxd`、`hexdump`非本工具目標——請使用位元組轉換器
不可見字元檢查在裸殼環境中僅限位元組層級明確顯示 U+200D 與 U+000A
在腳本中重複使用簡單,可在任何 shell 中執行需手動貼上,再複製出來
可離線運作僅在工具已預裝時可行可在任何現代瀏覽器分頁中執行

純量值、UTF-16 碼元與 UTF-8 位元組

對大多數工程師而言,重點的診斷在於字串包含哪些抽象字元,而非哪些位元組透過特定協定傳輸。é 字元是純量 U+00E9,但在 UTF-8 中會變成兩個位元組的序列 C3 A9,而在 JavaScript 內部的 UTF-16 表示中,由於 U+00E9 遠低於輔助平面切點,因此仍是單一碼元。以一個完整的例子來看 U+00E9:十六進位數字 0、0、E、9 組合起來為 0×4096 + 0×256 + 14×16 + 9 = 233(十進位),而 UTF-8 的雙位元組形式會將這十一個位元打包為 11000011 10101001,也就是 C3 A9。純量是 Unicode 編碼/解碼器所報告的內容,位元組則是 UTF-8 轉換器所報告的內容,而這兩個答案在各自的層級上都正確。

有趣的案例是像 😀(U+1F600)這類輔助平面字元。其純量值為 128512,遠高於 U+FFFF,因此 UTF-16 以代理對(D83D、DE00)表示,UTF-8 則將其寫成四個位元組(F0 9F 98 80)。這兩種編碼都不是純量本身。Unicode 編碼/解碼器報告的是純量值,並在反向輸入時拒絕代理區間中的任何內容,這避免了某個代理方使用錯誤的代理機制與自身往返保持「自洽」的陷阱。

正規化也不在討論範圍內。預先組成的 é(U+00E9)與分解後的序列 e 加上組合尖音符(U+0065 U+0301)在顯示上完全相同,但在本工具的輸出中仍是不同的序列。當你試著診斷為何檔名、搜尋結果或識別碼比對會失敗時,這樣的精確性正是重點。當問題從「是哪個字元」變成「是哪幾個位元組」時,適合的工具是 UTF-8 轉換器而非純量查詢,而其底層標準記載於 Unicode Code Charts

本工具不顯示的內容

Unicode 編碼/解碼器的設計刻意保持精簡。它不會查詢官方字元名稱、字集、易混淆狀態或語言意義,也不會切分字素簇(grapheme cluster)。上述 👩‍💻 例子是以三個純量呈現,雖然人在螢幕上讀到的是單一字素簇。關於字元名稱與屬性,Unicode Code Charts 仍是權威參考;至於解碼模式所接受的 JavaScript 風格 `\u{...}` 大括號標記法,其權威行為定義於 ECMAScript String.fromCodePoint。一個對語法範例很有用的搭配資源是 U+XXXX 與 \u{} 速查表,它深入探討本工具所接受的相同標記法。