在工作上,經常會碰到萬碼奔騰的狀況,常搞的我一個頭兩個大,還好經過一年多的努力,處理 i18n 的程式庫大致有了個基礎,雖不完整,但也堪用。我有試著讓這堆 i18n 的程式儘可能地一致有條理,但邊造槍邊上戰場,還是有很多地方雜草叢生,旁枝錯節,無法達到完美和諧。所以我也只能盡量,將「萬碼奔騰」化約為「五碼諧和」,便是其中的一個嘗試。

世界上的語言千萬種,應用在電腦上,各家都要有自己的一套,因而造成了「萬碼奔騰」的現況。雖然 Unicode 包容萬碼的理想極其崇高偉大,但是革命尚未成功,同志仍須努力。一天這個世界還不能大一統,身為師程工的我們,就必須要身先士卒,代替普羅大眾,面對萬碼侵襲。

還好,我們有「化約」這個武器,可以化繁為簡,萬宗歸五,將「萬碼」化約成「五碼」,挑出重點,各個擊破。哪五碼?RAW、MBCS、WCCS、U8CS 以及 CLCS 這五碼,各有各的花名,招式相同的,大家一條褲子一起穿,招式不同的,那就各顯本事,各展奇才。口說無憑,有表有真相。

編碼 字元 字串 說明
花名 型別 花名 型別
RAW - char str char* 無招勝有招,赤裸裸的 bytes 專門演出無碼劇。本應使用 byte_t 為其型別,但為與大多數的舊有 C 程式介面相容,故仍沿用 char
MBCS mbc char* mbs char* 用多個 bytes 表達一個 character 的編碼形式。某些 MBCS 需要 shift-state 的支援。對 C/C++ 來說,MBCS 對應到目前唯一的 global locale。
WCCS wcc wchar_t wcs wchar_t* 由 OS/Compiler 廠商決定的固定長度 Unicode 編碼,無法跨平台,兼有包容與速度的優勢。
U8CS u8c char* u8s char* UTF-8,為近年流行的跨平台之不固定長度 Unicode 編碼,未來的烏托邦,便是只剩下 WCCS 與 U8CS 這兩種碼的大一統世界。雖然現在還不是,但我們必須向著這個方向走。
CLCS lcc byte_t* lcs byte_t* CLCS 為 Custom Locale Character Set 的縮寫,即指定 locale 的編碼方式。當其他四碼都不行時,就只有靠他了。

為了分辨彼此,各招的名稱,必須內含各碼的花名。舉例來說,光就「長度」這一項,就可以分化成三個變化:有多少個 bytes、有多少個 characters 以及畫在終端機螢幕上時,佔用多少個基本寬度。將上表套用這三個變化,就成了以下各招的招型:

  • 有多少個 bytes:除了 RAW 字元以外,不論字元或字串,都有佔用 bytes 數的問題。其中,WCCS 字元佔用的 bytes 數為常數,其值各平台不一定相同。
    size_t adk_str_nbytes(const char* str);
    size_t adk_mbc_nbytes(const char* mbc);
    size_t adk_mbs_nbytes(const char* mbs);
    #define ADK_WCC_NBYTES sizeof(wchar_t)
    size_t adk_wcc_nbytes();
    size_t adk_wcs_nbytes(const wchar_t* wcs);
    size_t adk_u8c_nbytes(const char* u8c);
    size_t adk_u8s_nbytes(const char* u8s);
    size_t adk_lcc_nbytes(const char* loc, const byte_t* lcc);
    size_t adk_lcs_nbytes(const char* loc, const byte_t* lcs);
  • 有多少個 characters:對 RAW 編碼方式無意義。
    size_t adk_mbs_nchars(const char* mbs);
    size_t adk_wcs_nchars(const wchar_t* wcs);
    size_t adk_u8s_nchars(const char* u8s);
    size_t adk_lcs_nchars(const char* loc, const byte_t* lcs);
  • 畫在終端機螢幕上時,佔用多少個基本寬度:對 RAW 編碼方式無意義。對於傳統 CJK 來說,有所謂「寬字元」的存在,印在終端機螢幕上時,佔用普通字元的兩倍寬。
    size_t adk_mbc_width(const char* mbc);
    size_t adk_mbs_width(const char* mbs);
    size_t adk_wcc_width(wchar_t wcc);
    size_t adk_wcs_width(const wchar_t* wcs);
    size_t adk_u8c_width(const char* u8c);
    size_t adk_u8s_width(const char* u8s);
    size_t adk_lcc_width(const char* loc, const byte_t* lcc);
    size_t adk_lcs_width(const char* loc, const byte_t* lcs);

而例外狀況,就交由特殊的招式處理。好比說應付 C++ template parameter 於 coding-time 仍未知的字元、字串型別,我們可以有 TPCS,使用 tcc 與 tcs 的花名,自動於 compile-time 的 template parameter resolution 時,轉換為對應的 MBCS 或 WCCS 型別。或者,為了處理與大五碼 (big5) 相容的特殊需求,我們可以有 B5CS,使用 b5c 與 b5s 的花名,針對 wcc/u8c/lcc/wcs/u8s/lcs 與 b5c/b5s 的可轉換性,進行處理。

如此,透過「萬碼化五碼」及「花名制」,就算與字元處理的招式再多,面對萬碼,仍然還是只有這五碼,簡單易懂,方便使用。