國際化域名編碼

國際化域名編碼（Punycode）是一種表示統一碼和 ASCII 碼的有限的字元集。例如中文 “上海” 會被編碼為 “fhqz97e”。

國際化域名編碼的目的是在於國際化域名標籤（IDNA）的框架中，使這些（多語言）的域名可以編碼為 ASCII。編碼語法在文件 RFC 3492 中規定。

編碼過程

中說明：國際化域名編碼是一種稱為 Bootstring 的更普遍的演算法例項，它允許由部分基本的編碼集合組成的字串唯一表示由更大編碼集合組成的任何字串。配合統一碼文字的特性，國際化域名編碼定義了一般 Bootstring 演算法的引數。本節以德語字串 "bücher"（書籍）為例，演示國際化域名編碼的編碼過程，該字串被編碼為 "bcher-kva"。

1.1 分離 ASCII 字元

首先，字串中的所有 ASCII 字元將被直接從輸入複製到輸出，任何其他字元將被跳過。例如，"bücher" 被複製為 "bcher"。如果有任何字元被複制（即輸入中至少有一個 ASCII 字元），則在輸出中附加一個 ASCII 連字元（例如，"bücher" 複製為 "bcher-"，其中 "ü" 不是 ASCII 字元，不發生複製）。

由於連字元本身是 ASCII 字元，因此可以出現在輸入中，並將被複制到輸出中。這並不會引起歧義，因為如果輸出包含連字元，被新增的連字元總是最後一個，這標誌著 ASCII 字元的結束。

1.2 編碼非 ASCII 字元

對於輸入的非 ASCII 字元，依統一碼中碼位由低至高順序，編碼器會模擬將非 ASCII 字元一一插入回去除非 ASCII 字元後的字串過程，

計算每個非 ASCII 字元對應的三個數字 i、n 和 h：

i 表示該非 ASCII 字元在輸入字串中的以 0 為開始索引的插入位置（類似於如 C 語言等的程式語言，用 0 來表示該非 ASCII 字元是輸入字串的第一個字元）。
n 表示該非 ASCII 字元在統一碼中的碼位，減去 127（127 為最後一個 ASCII 字元）
h 表示該非 ASCII 字元插入時可能有的插入位置。如 "ü" 要插入回 "bcher" 中，可能有 "übcher" 到 "bcherü"6 個位置可供插入。

編碼器隨後計算乘法 n*h+i 並將得到的數字編碼為一串 36 進位制的可變長度數字，將這些數字轉化為 ASCII 碼，並將結果附加到輸出字串後。

每次計算後，該非 ASCII 字元會插入回去除非 ASCII 字元後的字串，因此 h 會增加 1。

並且在計算下一個非 ASCII 字元的統一碼碼位時，該碼位需減去上一個非 ASCII 字元碼位。

編碼方式為：0 -> 'a', ..., 25 -> 'z', 26 -> '0', ..., 35 -> '9', 數字的數字按小端序排列。

此處的 36 進位制的編碼並非通常意義下的下 36 進位制整數，而是一種可變長度的整數。每個數字的最重要（most significant）的位（例如數字 "123" 中的 "1"）在沒有上下文的情況下是可以識別的。因此，多個數字可以無需分隔地連線，不會影響原始數字的識別和提取。但此處每位數字進位的門檻閥值不同，並非每位皆是 36 進位。

1.3 用於國際化域名的 ACE 字首

為了防止非國際域名中的連字元被國際化域名編碼解碼，會在在國際化域名中的國際化域名編碼序列前加上字串 xn--。這被稱為 ACE（ASCII Compatible Encoding）。

因此，域名 "bücher.tld" 將會以 ASCII 形式表示為 "xn--bcher-kva.tld"。

1.4 解碼器

解碼器是具有兩個狀態變數 i 和 n 的有限狀態機。

i 是字串的索引，範圍從 0（代表可能插入的開始）到擴充套件字串的當前長度（代表可能插入的結束）。i 從 0 開始。

n 從 128 開始（128 表示第一個非 ASCII 的碼位）。

狀態遞增是一個單調函式。一個狀態轉移要麼增加 i，如果 i 達到最大，就重置 i 為零並增加 n。下一個狀態轉移時，繼續增加 i。 在每個狀態下，由 n 表示的碼位要麼被插入，要麼不被插入。

編碼器生成的數字代表在插入之前要跳過多少可能的結果。

在字串 "bcher" 中，有六個位置可以插入一個字元（包括第一個字元前和最後一個字元後）。在最後一個 ASCII 碼位（127）和 "ü"（252，參見拉丁字母補充 - 1）之間有 124 個碼位。有一個 "ü" 的插入位置必須跳過（位置 0，即在 'b' 之前）。

因此，在到達正確位置前，解碼器將跳過共（6×124）+1=745 個可能的插入位置。在該字元插入後，將有七個可能的地方插入下一個字元。

1.5 將碼號重新編碼為 ASCII 序列

國際化域名編碼使用通用變長整數來表示這些值。例如，"kva" 將用來表示碼號 745。

小端序的數字系統允許不需要單獨的分隔符的變長編碼：低於閾值的數字標誌著它是最重要的數字，即是數字的結束。為了提高效率，閾值取決於數字的位置和以前的插入。同時，數字的權重也會變化。

在使用 36 個符號的數字系統中，不區分大小寫的 'a'-'z' 表示十進位制數字 0-25，'0' 到 '9' 表示十進位制數字 26-35。因此，"kva"，表示十進位制數字序列 "10 21 0"。

為了解碼這串符號，需要閾值序列。本例中，閾值序列為（1，1，26，26，...）。最不重要的數字（least-significant digit）的權重（或位置值）總是 1，具有權重 1 的 'k' 等於 10（1×10=10）。下一個數字的權重取決於第一個閾值：一般來說，對於任意 n，第 n+1 個數字的權重是第 n 個數字的權重乘以（36 - 第 n 個數字的閾值）。所以第二個符號的權重是 36 減去前一個閾值，即為 35（36-1=35）。因此，前兩個符號 'k' 和 'v' 之和為 10×1+21×35。由於第二個符號不小於其閾值 1，因此還有更多符號。然而，由於本例中第三個符號是 'a'（a=0），可以忽略其權重的計算。因此，"kva" 代表十進位制數字（10×1）+（21×35）=745。閾值本身由一個演算法為每個編碼字元序列決定，使其保持在 1 和 26 之間（包括 1 和 26）。字元的大小寫可以用來提供關於原始字串的大小寫資訊。由於特殊字元按法編碼算的碼位排序，在插入 "bücher" 中特殊字元 "ü" 時，第一種可能是編碼為 "bcher-kvaa" 的 "büücher"，第二種可能是編碼為 "bcher-kvab" 的 "bücüher"，等等等等。在編碼為 "bcher-kvae" 的 "bücherü" 之後是代表插入 ý 的編碼，即 ü 之後的統一碼字元，從編碼為 "bcher-kvaf" 的 "ýbücher" 開始（與編碼為 "bcher-jvab" 的 "übücher" 不同），等等。

為了使編碼、解碼演算法簡單，編碼時並沒有試圖阻止一些編碼不允許的統一碼值：然而，解碼時應該檢查這些值。

國際化域名編碼的設計使其成可以在所有指令碼中工作，並透過在操作過程中嘗試適應字串中的字元集範圍進行自我最佳化。它針對字串由包含 0 個或更多 ASCII 字元和來自其他腳本系統的字元組成的情況進行了最佳化，同時也能處理任意的統一碼字串。在使用 DNS 時需要注意，域名字串被認為已經使用進行了規範化處理，並且頂級域名在被國際化域名編碼之前被假設已經根據官方註冊的語言表進行了處理。同時，DNS 協議對輸出國際化域名編碼字串的可接受長度也有所限制。

例子

下表展示了不同型別輸入的國際化域名編碼的例子。