頗特殊的需求: 一個跨平台整合在傳遞以GUID為Primary Key資料時,對方的參數欄位只接受最長30個字元,即使使用16進位數字表示法(例如: 4854c292c333480890f916d1a062b8e3),GUID字串也長達32字元,超出限制。另外想一種不會重複的識別編號法則是種解法,但要做到GUID等級的唯一性得付出不少代價。因此,另一個思考方向是如何用較短的字串長度表示GUID,評估是較省力的做法。
要比16進位表示法更簡短,最簡便的做法是將其GUID先轉為byte[],再用Base64編碼轉為字串。例如: 4854c292c333480890f916d1a062b8e3 可以轉換成 ksJUSDPDCEiQ+RbRoGK44w==,只要24個字元。但有個問題,對方系統的資料庫定序(Collation)被設為不分大小寫,而Base64編碼需大小寫有別,唯一限制及查詢比對可能出問題。
原本想拿出自己在VB6時代發明的36進位表示去(使用A-Z及0-9共36個英數元表示二進位資料),但轉念一想,如果有標準可循,還是別自己造輪子好。查詢後,真的有所謂的Base32編碼,而且還是RFC標準(RFC 4648)! Base32只用A-Z及2-7共32個字元表示,編碼結果比Base64長約20%,但好處是:
只是Base32的應用不若Base64普遍,在.NET基本類別庫無現成可用的函數編解碼。所幸,既是公開標準,就不難找到前輩先進寫好的範例。我在stackoverflow找到一個實作範例,但測試發現它在預估字串長度時計算有誤差,會有多餘的Padding字元"=",但這只需要小小調整就可改善: [已留言建議]
static void Main(string[] args)
List<Guid> guidPool = new List<Guid>();
for (int i = 0; i < 10000; i++)
guidPool.Add(Guid.NewGuid());
Guid restored = Guid.Empty;
Stopwatch sw = new Stopwatch();
foreach (Guid uid in guidPool)
str = GetShortGuidString(uid);
restored = ParseShortGuidString(str);
if (!uid.Equals(restored))
throw new ApplicationException("Test Failed!");
Console.WriteLine("Base64 Version in {0:N}ms,\n {1:N} -> {2}",
sw.ElapsedMilliseconds, restored, str);
foreach (Guid uid in guidPool)
str = GetShortGuidString(uid, true);
restored = ParseShortGuidString(str, true);
if (!uid.Equals(restored))
throw new ApplicationException("Test Failed!");
Console.WriteLine("Base32 Version in {0:N}ms,\n {1:N} -> {2}",
sw.ElapsedMilliseconds, restored, str);
static string GetShortGuidString(Guid uid, bool useBase32 = false)
return Base32Encoding.ToString(uid.ToByteArray());
return Convert.ToBase64String(uid.ToByteArray());
static Guid ParseShortGuidString(string s, bool useBase32 = false)
return new Guid(Base32Encoding.ToBytes(s));
return new Guid(Convert.FromBase64String(s));
推測目前找到的Base32編碼邏輯,最佳化程度不及.NET內建的Convert.ToBase64String,故Base32執行速度較Base64慢3倍,但1萬筆只需0.04秒,在整合應用時成為瓶頸的可能性不高,測試驗證可行。