什麼是 Base62?
Base62 是一種只用在任何地方貼上都能存活的字元來書寫數字的方式:十個數字、26 個大寫字母和 26 個小寫字母。沒有標點、沒有空格、沒有填充字元,沒有任何需要先轉義才能放進 URL、檔名、Cookie 或資料庫鍵的東西。10 加 26 加 26 等於 62,名字由此而來。
30 秒版本:數字 12345 寫作 3D7,1234567890 寫作 1LY7VK。反方向是同一個運算的逆過程,但必須使用產生該短碼的確切字母表 — 換一個字母表,同樣的六個字元會變成另一個數字,而且沒有任何報錯。
本頁做兩件相關的事,它們的區別比本頁其他任何事情都重要:
- 整數模式把輸入當作數字。像
1234567890 這樣的十進位制 ID 變成 1LY7VK,輸入的前導零不屬於值本身,因此不會回來。
- 位元組模式把輸入當作位元組串。位元組被當作一個大端大整數讀入,再按 62 整除,是對另一個量做的同樣算術。這裡前導零位元組的數量會保留下來,每個零位元組對應一個零字元。
62 從哪裡來,代價是什麼
62 不是 2 的冪,僅此一條事實就決定了其他一切。人們熟悉的編碼是把輸入切成固定位元組:4 位元印成一個十六進位制字元,6 位元印成一個 Base64 字元,5 位元印成一個 Base32 字元。Base62 做不到這一點,因為沒有整數的位元數能匹配一個 62 值的字元 — 5 位元只能表示 32 個值,6 位元是 64。所以 Base62 走的是算術路線:把整個輸入讀成一個整數,再以 62 為底印出。
於是一個字元承載 log2(62) = 5.954 位元,非常接近 Base64 的 6;在十進位制模式下,一個字元頂替 log62(10) = 1.792 個十進位制位。在決定長度之前,有幾個值得知道的後果:
| 編碼長度 | 可表示的值 | 實際意義 |
| 4 個字元 | 624 = 14,776,336 | 足夠一個不超過八位數的計數器使用,且塞得進一個電話號碼的寬度。 |
| 6 個字元 | 626 = 56,800,235,584 | ID 短碼的常見形態。十位十進位制 ID 六個字元即可容納。 |
| 7 個字元 | 627 = 3,521,614,606,208 | 比 32 位計數器的範圍高出兩個數量級,正好是一個短連結別名的寬度。 |
| 11 個字元 | 6211 = 52,036,560,683,837,093,888 | 超過 264 = 18,446,744,073,709,551,616,完整的 64 位數十一字元即可容納。 |
反過來看,1,000 個十進位制位需要約 558 個字元,4,096 個十進位制位需要 2,285 個。節省是實在的,但有上限:Base62 把十進位制數縮短約 44%,而且永遠比不過最密的列印編碼,因為每字元 5.954 位元就是比 6.4 或 6.5 少。
整數模式:前導零的下場
整數只有一種規範的書寫形式,它以字母表的零字元開頭 — 在兩種數字優先的排序中是 0,在字母優先的排序中是 A。第一個有效字元左側的一切都是填充,而填充不屬於值:00012345 和 12345 都變成 3D7。如果需要固定寬度 — 比如一個要在列中對齊的七字元短碼 — 本頁的把結果填充到固定寬度輸入框會重複零字元直到達到寬度,所以寬度為 7 時 12345 在數字優先字母表中印作 00003D7,在字母優先字母表中印作 AAAADNH。填充只是外觀:那些前導字元對值毫無貢獻,解碼任何一個字串都還原出 12345,因為無論用哪個字元書寫零,0 × 626 和 0 × 625 都是零。
因此解碼帶填充的字串同樣可行,頁面會報告前導字元中有多少個是零字元,讓「短碼有七個字元」和「值只需要三個字元」之間的差別不再成謎。
位元組模式:對位元組串做同樣的算術
位元組模式把輸入位元組當作一個大端整數讀入 — 無論多長,整個位元組串被視為一個數 — 再把這個數以 62 為底印出。一個位元組有 8 位元,而一個字元只承載其中 5.954 位元,所以位元組模式不是壓縮:它需要輸入尺寸的約 134%,比 Base64 的 133% 略差。以上方尺寸表使用的 92 位元組樣本為例,92 位元組印作 124 個 Base62 字元,Base64 同樣印作 124 個(其中 123 個在它的 = 填充之前)。Base62 在密度上輸給 Base64,在字元安全性上贏回來,這正是每個短連結和邀請碼有意做出的取捨。
前導零位元組會被保留,而這一點在兩個方向上都容易出錯。兩個前導零位元組後跟 0x01,在數字優先字母表中印作 001,在字母優先字母表中印作 AAB,因為前導字元書寫零,且每個零位元組對應一個。單個 0x00 的位元組串印作單個字元 0,單個 0x01 印作 1。這是位元組模式和整數模式運行同一演算法卻看起來不同的唯一一處。
這一切的代價是位元組模式繼承了算術的平方級耗時:1,024 位元組估計約三分之一秒,2,048 位元組約 1.3 秒,與上方限制表給出的數字相同。這就是本頁位元組模式出方向封頂 2,048 位元組、回方向封頂 2,048 字元的原因 — 2,048 位元組印出來約 2,751 個字元,所以約 1,520 位元組是能完整通過本頁往返的最大載荷 — 也是本頁位元組模式只作小值參考、不能替代 Base64 編碼/解碼 或 Base91 編解碼 處理真實檔案的原因。
三種字母表,以及為什麼字母表就是全部
字母表的字元是一個固定序列,一個字元的值就是它在這個序列中的位置。挪動序列就改變了每個字元的含義。三種排序被廣泛使用,下面是同一個數在每一種中的三種寫法:
| 十進位制 | 0-9 然後 A-Z 然後 a-z | 0-9 然後 a-z 然後 A-Z | A-Z 然後 a-z 然後 0-9 |
| 0 | 0 | 0 | A |
| 61 | z | Z | 9 |
| 62 | 10 | 10 | BA |
| 12,345 | 3D7 | 3d7 | DNH |
| 1,234,567,890 | 1LY7VK | 1ly7vk | BViHfU |
把最後三列當成一個字母表來讀,它是一個自洽的方案。把它們當成可互換的來讀,它就散架了,因為解碼對字母表是全盲的,而且盲得最徹底:輸入的任何字元都在全部三個字母表裡,所以錯誤的字母表不會產生任何報錯,只會產生另一個數字。
| 一個短碼 | 按 0-9 A-Z a-z 讀 | 按 0-9 a-z A-Z 讀 | 按 A-Z a-z 0-9 讀 |
| 3D7 | 12,345 | 13,957 | 211,665 |
| 1LY7VK | 1,234,567,890 | 1,624,950,792 | 48,723,527,772 |
大小寫是同一個陷阱更尖銳的版本。Base62 在每種排序中都大小寫敏感,所以把短碼小寫化會悄悄改寫它:1LY7VK 在數字優先字母表中是 1,234,567,890,而 1ly7vk 在同一字母表中是 1,624,950,792 — 它恰好又是 1,234,567,890 在數字後小寫字母的字母表中的值,因為那個排序正是數字優先排序交換兩個字母大小寫後的結果。零字元也不共享:兩種數字優先排序把零寫作 0,而字母優先排序把它寫作 A,所以那個字母表根本不以數字開頭。
Base62 與其他編碼的對比
| 編碼 | 每字元位元數 | 相對輸入的體積 | 備註 |
| Base16(十六進位制) | 4 | 200% | 每位元組兩個字元,一眼可讀,無需查字母表。 |
| Base32 | 5 | 160% | 大小寫不敏感、適合人口述,是這組裡最浪費的。 |
| Base36(0-9 a-z) | 約 5.17 | 約 155% | 只有數字和小寫字母,因此能挺過小寫化流水線。12345 寫作 9ix。 |
| Base58 | 約 5.86 | 約 137% | 為地址而非密度而生:去掉四個易混淆字元而不是給它們排序。見 Base32 / Base58 編碼轉換器。 |
| Base62 | 約 5.95 | 約 134% | 每個字元都是數字或字母,永遠無需轉義。密度介於 Base58 與 Base64 之間。 |
| Base64 | 6 | 133% | 各處的預設選擇,代價是 +、/ 和 = 這三個強制轉義的字元。見 Base64 轉換器。 |
| Ascii85 / Base85 | 6.4 | 125% | 4 位元組 5 字元,但按 85 整除,其字母表需要加引號。見 Ascii85 轉換器。 |
| Base91 | 約 6.5 | 約 123% | 這組裡密度最高的,也是輸出未經轉義就貼上最不安全的。見 Base91 編解碼。 |
百分比是四捨五入後的漸近開銷;對一個真實 92 位元組輸入的實測字元數在本頁上方的尺寸表裡。整數模式下比較反過來看,因為輸入是十進位制文本而非位元組:此時 Base62 比它替換的數字短約 44%,Base36 短約 36%,這就是 ID 短碼通常用 Base62 或 Base36 而非 Base58 的原因。
Base62 不是什麼
- 不是標準。沒有 RFC,也沒有規範。從未有標準組織定義過 Base62,這正是字母表沒有保證的原因,也是本頁把三種排序全部印出而不是悄悄挑一個的原因。
- 不是加密。只是換了一次基,僅此而已。任何知道字母表的人一遍就能還原,沒有金鑰、沒有鹽、沒有任何秘密。
- 不是壓縮。位元組模式讓資料增長約三分之一。整數模式看似壓縮,只是因為十進位制文本本來就是寫數字的低效方式。
- 不是大小寫不敏感。Base32 可以小寫後再讀回來;Base62 不行。一個經過大小寫規範化的系統的短碼就是損壞的短碼。
- 不能自校驗。沒有校驗和也沒有校驗字元,單個錯誤字元會給出一個看起來有效但值不同的結果。如果這很重要,Bitcoin 排序自帶校驗和 — 見 Base32 / Base58 編碼轉換器 的 Base58Check 一節。
- 不是定長。字元數隨值增長,一個從 1 漲到 627 的計數器會從一個字元變成八個。需要穩定列寬時請填充到固定寬度。
真正咬人的陷阱
- 十有八九的謎團,答案都是字母表。如果另一個工具對同一個數給出不同的短碼,先逐字元比較兩邊的字母表,再懷疑是否有 bug。上表顯示了同一個值在三個字母表中的樣子,三者全都正確。
- 錯誤的字母表靜默失敗。它沒有報錯路徑:
3D7 可以解碼成 12,345、13,957 或 211,665,只取決於假定了哪種排序。檢查首字元(它最可能是填充零),並檢查字母表開頭是數字還是字母。
- 前導零有兩種不同含義。整數模式中
00012345 和 12345 是同一個數,印出來一模一樣。位元組模式中前導零位元組是資料,會印作一個額外的零字元。丟掉其中一個字元就改變了位元組串。
- 大小寫規範化會毀掉短碼。電子郵件地址、主機名或 Windows 路徑都可能在傳輸中被小寫化。Base62 短碼挺不過這種處理,這正是 Base36 仍然存在的原因。
- 長輸入不只是大,還慢。轉換是對整個輸入一次性做的算術,輸入長度翻倍,時間翻四倍。本頁在超過實測限制時直接拒絕而不是掛起;上方尺寸表給出了實測數字。
- Base62 字串本身證明不了任何事。既然幾乎任何數字字母串都能無抱怨地解碼,任意 URL 別名都會解出某個數字。短碼只有當發放它的系統同時儲存了對映時才有意義。
快速問答
為什麼另一個 Base62 工具對同一個數給出不同的短碼?
因為字母表不同。Base62 沒有可供分歧的規範,實現者各選一種排序:數字和大寫在前、數字和小寫在前,或字母在數字之前。切換本頁頂部的字母表下拉框,就能並排看到三種答案。
Base62 在 URL 裡安全嗎?
安全,在全部三種排序中都安全。全部 62 個字元都是無需轉義的字母和數字,查詢串裡沒有任何一個會被展開成百分號三元組,也沒有任何一個會終結 URL、路徑段或 HTML 屬性。這正是短連結用 Base62 而非 Base64 的全部原因。
Base62 和 Base64 哪個更小?
對位元組而言,Base64 略小一點:約為輸入的 133%,Base62 約 134%。對上面的 92 位元組樣本,兩者持平,都是 124 個字元。對十進位制數字而言,Base62 把文本縮短約 44%,這才是實際的收益所在。
能用本頁解碼 Base64 字串嗎?
不能,而且它不會告訴你失敗了。兩個字母表在全部 62 個字元上重疊,所以 Base64 字串通常在本頁也是「合法」輸入,只會解出另一個數字。解碼 Base64 請用 Base64。
Base62 短碼有最大長度嗎?
理論上沒有,演算法沒有上限。實際上本頁止步於 4,096 個十進位制位或 2,048 位元組,因為它背後的算術是平方級的,標籤頁會卡死而螢幕上沒有任何解釋。本頁上方的限制表列出了實測數字。
怎麼知道本頁的結果是對的?
按自測。它會執行每個已記錄的示例、0 到 64 的每個位元組長度和一組十進位制數字,遍歷全部三種字母表,把返回值與輸入比較,並複查每個字母表的 62 個字元在 URL 中是否全部安全。示例按鈕還會把它的三個結果與本頁資料檔案中記錄的值對比,那些值已由本站測試工具中第二套獨立的大整數實現驗證過。
標準與致謝
這裡沒有可供實現的標準:Base62 是一種約定,不是規範,上面印出的三種字母表就是真實軟體中出現的那三種。本頁用本站自己的大整數例程實現它,直接在數字陣列上做普通算術,因此任何尺寸都不會被 JavaScript 的 number 型別限制。
本頁文字為本站撰寫。頁面上每個算過的例子都由頁面自己的程式碼產生,並經本站測試工具中第二套基於原生大整數的獨立實現驗證;同一實現也驗證了資料檔案中的示例表和尺寸表。