我知道很多人一直是這樣做的, 可是我從沒搞懂為什麼在 windows 平台上, 用上了 _TCHAR, _TEXT 再 define 個 _UNICODE. 或是用上了 whar_t 與 std::wstring. 就能算是 Unicode 化的程式?
Unicode 說穿了就是一種 數字到字元的對應. 而 UTF-8/UTF-16/UTF-32 等則是 Unicode 這種數字的編碼方式, 也就是同一個 (能對應 Unicode 字元的) 數字在不同的編碼下會是不同的 byte sequence.
在 WIN32 平台上, wchar_t 是個 16-bits 的型別. [1] 很明顯地, 它最多只能 (事實上應該也是) 對應到 Unicode 中的 UTF-16 編碼.
UTF-16 定義了約十一萬個字元. [2] Wikipedia 也提到 UTF-16 是一種可變長度的編碼. 由此可見, 一個 WIN32 下的 whar_t 沒有辦法總是以一個 16-bits (最大值為 2^16 -1, 也就是 65535) 的數字來代表十一萬種字元.
由此可以知道一個 WIN32 的 wchar_t 是無法完全對應到一個 UTF-32 encoding 的字元. 所以類似下面的操作:
utf_32_char_t get_utf_32_char(const std::wstring& wstr, size_t n)
{
assert(wstr.size() > n);
return wstr[n];
}
並無法正確的對應到位於 n 位置的 Unicode 字元. 程式還是必須從 wstr 的頭掃描到第 n 個 Unicode 才能正確將一個 UTF-16 編碼的字元 (1 or 2 wchar_ts)轉為 UTF-32 的字元. Windows 的 wchar_t 的優點或用處到底在那裡?
我看來是這只是偷懶的行為, 假裝每一個 WIN32 的 wchar_t 可以代表任意一個 UTF-16 unicode 的字元. 跟鴕鳥把頭躲進沙坑逃避現實的的行為沒什麼差異. 與 UTF-32 比起來, 除了能節省空間外, 似乎一點其他好處也沒有.
PS. 我對 Unicode 沒有特別研究, 上面應該有很多 Unicode 術語都用錯了. 但 concept 應該沒問題.
[Update: Nov 20, 2007 at 14:40]
感謝 Jeff 在回應裏面提到 Windows 的 wchar_t 是對應到 UCS-2, 而不是 UTF-16. [3] 這下子我可能有點搞懂了. UCS-2 的確就只能表達 Basic Multilingual Plane (BMP), 也就是 Unicode 中的 0×0000~0xFFFF. 以一個 16-bits 的整數是來表達 BMP 是 just make.
無法表達超出 BMP 的 Unicode, 這是 WIN32 的先天限制? 所謂的 Unicode API 也不真的那麼 Unicode, 是嗎?
好吧, 如果永遠活在 WIN32 的世界下這樣似乎也可行. 可是如果是從別的地方 (譬如透過網路) 傳過來一個非 BMP 的 Unicode 的字元時怎麼辦? 下面的 code 該怎麼寫?
wchar_t utf8_to_ucs2(const char* utf8)
{
if(is_bmp(utf8) == true)
{
wchar_t usc2;
// fine here, we just need an algorithm to transcode
// a UTF-8 encoded BMP code point (character) to UCS-2
return usc2;
}
else
{
// what goes here?
// what to do if the code point is not a BMP?
}
}
[Update: Sep 3, 2008 at 13:40]
Follow up is available in a more recent post – More on Unicoding.
- Default 似乎還不是 built-in/primitive, 而是個 得 include stddef.h/stdlib.h 或… 的 typedef [↩]
- 另外在 rfc2781 的 Section 2 也說明了 UTF-16 無法表示值大於 0x10FFFF 的 Unicode [↩]
- What is the difference between UCS-2 and UTF-16? [↩]
« 忙茫盲? 拒絕 Singleton »

wchar_t是「fixed size」,不同平台的大小不同。如win32是UCS2-LE,長2 bytes,而UNIX通常是UCS4,長4 bytes。所以,get_utf_32_char()是不會有問題的。
按照C++98,wchar_t是build-in type,但按照C99,wchar_t卻不是,故得#include某些header以取得typedef後的型別。由於一般C/C++ compiler都是做在一起的,故比較新的compilers會把wchar_t作成build-in/primitive type。
VC6不夠新,所以wchar_t不是build-in/primitive type,也因此用ostream時,comiler無法分辨wchar_t與short,而無法印出wchar_t字元。
也因為VC6的wchar_t不是build-in/primitive type,所以後來的MS C/C++ compilers,預設wchar_t也都不是build-in/primitive type。不過,可以在project setting裡調整,將wchar_t試作build-in/primitive type就是了。
Hi Jeff,
第二個回應的歷史我大致清楚.
針對第一個回應我還有接續的問題, 寫在 update 裏面. 感謝解惑.
在Google Chrome source code, string16.h 的comment看到:
// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
// at run time, because it calls some functions (like wcslen) that come from
// the system’s native C library — which was built with a 4-byte wchar_t!
// It’s wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it’s
// entirely improper on those systems where the encoding of wchar_t is defined
// as UTF-32.
//
其中,”It’s wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it’s entirely improper on those systems where the encoding of wchar_t is defined as UTF-32.”。
看起來似乎與站長的見解不盡相同。註:本人對於UNICODE沒有研究,只會呆呆地用std::wstring and wchar_t 來寫UNICODE 程式,^^!
Here is a link to that string16.h
http://src.chromium.org/viewvc/chrome/trunk/src/base/string16.h?revision=1464&view=markup
Hi Sam,
Thanks for the link.
我完全同意你 quote 的那句話, 也在文中補上了 UTF-16 較 UTF-32 節省空間.
但我還是認為用 UTF-16 不如採用 UTF-32. 若真要節省空間, 尤其是在大部份字元是在 ascii 定義範圍之內, 如程式碼, 那更應採用 UTF-8.
程式好寫用 UTF-32, 若空間有限則選 UTF-8.
[Update: Mar 3, 2009]
這句話一點都沒錯:
問題是為什麼要存成 UTF-16? 用 UTF-32 (
wchar_t) 或 UTF-8 (char) 就好了啊~