字符,字節和編碼, ASCII碼, DBCS碼,SBCS碼 與Unicode碼
字符,字節和編碼, ASCII碼, DBCS碼,SBCS碼 與Unicode碼https://www.regexlab.com/zh/encoding.htm
1. 編碼問題的由來,相關概念的理解
1.1 字符與編碼的發展
從計算機對多國語言的支持角度看,大致可以分為三個階段:
係統內碼 | 說明 | 係統 | |
階段一 | ASCII | 計算機剛開始隻支持英語,其它語言不能夠在計算機上存儲和顯示。 | 英文 DOS |
階段二 |
ANSI編碼 (本地化) |
為使計算機支持更多語言,通常使用 0x80~0xFF 範圍的 2 個字節來表示 1 個字符。比如:漢字 '中' 在中文操作係統中,使用 [0xD6,0xD0] 這兩個字節存儲。 不同的國家和地區製定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個字節來代表一個字符的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文係統下,ANSI 編碼代表 GB2312 編碼,在日文操作係統下,ANSI 編碼代表 JIS 編碼。 不同 ANSI 編碼之間互不兼容,當信息在國際間交流時,無法將屬於兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。 |
中文 DOS,中文 Windows 95/98,日文 Windows 95/98 |
階段三 |
UNICODE (國際化) |
為了使國際間信息交流更加方便,國際組織製定了 UNICODE 字符集,為各種語言中的每一個字符設定了統一並且唯一的數字編號,以滿足跨語言、跨平台進行文本轉換、處理的要求。 | Windows NT/2000/XP,Linux,Java |
字符串在內存中的存放方法:
在 ASCII 階段,單字節字符串使用一個字節存放一個字符(SBCS)。比如,"Bob123" 在內存中為:
42 6F 62 31 32 33 00 B o b 1 2 3 \0
在使用 ANSI 編碼支持多種語言階段,每個字符使用一個字節或多個字節來表示(MBCS),因此,這種方式存放的字符也被稱作多字節字符。比如,"中文123" 在中文 Windows 95 內存中為7個字節,每個漢字占2個字節,每個英文和數字字符占1個字節:
D6 D0 CE C4 31 32 33 00 中 文 1 2 3 \0
在 UNICODE 被采用之後,計算機存放字符串時,改為存放每個字符在 UNICODE 字符集中的序號。目前計算機一般使用 2 個字節(16 位)來存放一個序號(DBCS),因此,這種方式存放的字符也被稱作寬字節字符。比如,字符串
"中文123" 在 Windows 2000 下,內存中實際存放的是 5 個序號, 一共占 10 個字節:
2D 4E 87 65 31 00 32 00 33 00 00 00 // 在 x86 CPU 中,低字節在前 中 文 1 2 3 \0
1.2 字符,字節,字符串
理解編碼的關鍵,是要把字符的概念和字節的概念理解準確。這兩個概念容易混淆,我們在此做一下區分:
概念描述 | 舉例 | |
字符 | 人們使用的記號,抽象意義上的一個符號。 | '1', '中', 'a', '$', '¥' |
字節 | 計算機中存儲數據的單元,一個8位的二進製數,是一個很具體的存儲空間。 | 0x01, 0x45, 0xFA |
ANSI 字符串 |
在內存中,如果“字符”是以 ANSI 編碼形式存在的,一個字符可能使用一個字節或多個字節來表示,那麼我們稱這種字符串為 ANSI 字符串或者多字節字符串。 |
"中文123" (占7字節) |
UNICODE 字符串 |
在內存中,如果“字符”是以在 UNICODE 中的序號存在的,那麼我們稱這種字符串為 UNICODE 字符串或者寬字節字符串。 |
L"中文123" (占10字節) |
由於不同 ANSI 編碼所規定的標準是不相同的,因此,對於一個給定的多字節字符串,我們必須知道它采用的是哪一種編碼規則,才能夠知道它包含了哪些“字符”。而對於 UNICODE 字符串來說,不管在什麼環境下,它所代表的“字符”內容總是不變的。
1.3 字符集與編碼
使用哪些字符。也就是說哪些漢字,字母和符號會被收入標準中。所包含“字符”的集合就叫做“字符集”。各個國家和地區所製定的不同 ANSI 編碼標準中,都隻規定了各自語言所需的“字符”。比如:漢字標準(GB2312)中沒有規定韓國語字符怎樣存儲。這些 ANSI 編碼標準所規定的內容包含兩層含義:
- 使用哪些字符。也就是說哪些漢字,字母和符號會被收入標準中。所包含“字符”的集合就叫做“字符集”。
- 規定每個“字符”分別用一個字節還是多個字節存儲,用哪些字節來存儲,這個規定就叫做“編碼”。
各個國家和地區在製定編碼標準的時候,“字符的集合”和“編碼”一般都是同時製定的。
因此,平常我們所說的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”這層含義外,同時也包含了“編碼”的含義。
“UNICODE 字符集”包含了各種語言中使用到的所有“字符”。
用來給 UNICODE 字符集編碼的標準有很多種,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。
1.4 常用的編碼簡介
簡單介紹一下常用的編碼規則,為後邊的章節做一個準備。在這裏,我們根據編碼規則的特點,把所有的編碼分成三類
分類 | 編碼標準 | 說明 |
單字節字符編碼 | ISO-8859-1 | 最簡單的編碼規則,每一個字節直接作為一個 UNICODE 字符。比如,[0xD6, 0xD0] 這兩個字節,通過 iso-8859-1 轉化為字符串時,將直接得到 [0x00D6, 0x00D0] 兩個 UNICODE 字符,即 "ÖÐ"。 反之,將 UNICODE 字符串通過 iso-8859-1 轉化為字節串時,隻能正常轉化 0~255 範圍的字符。 |
ANSI 編碼 | GB2312,BIG5,Shift_JIS, ISO-8859-2 |
把 UNICODE 字符串通過 ANSI 編碼轉化為“字節串”時,根據各自編碼的規定,一個 UNICODE 字符可能轉化成一個字節或多個字節。 反之,將字節串轉化成字符串時,也可能多個字節轉化成一個字符。比如,[0xD6, 0xD0] 這兩個字節,通過 GB2312 轉化為字符串時,將得到 [0x4E2D] 一個字符,即 '中' 字。 “ANSI 編碼”的特點: 1. 這些“ANSI 編碼標準”都隻能處理各自語言範圍之內的 UNICODE 字符。 2. “UNICODE 字符”與“轉換出來的字節”之間的關係是人為規定的。 |
UNICODE 編碼 | UTF-8,UTF-16, UnicodeBig | 與“ANSI 編碼”類似的,把字符串通過 UNICODE 編碼轉化成“字節串”時,一個 UNICODE 字符可能轉化成一個字節或多個字節。 與“ANSI 編碼”不同的是: 1. 這些“UNICODE 編碼”能夠處理所有的 UNICODE 字符。 2. “UNICODE 字符”與“轉換出來的字節”之間是可以通過計算得到的。 |
我們實際上沒有必要去深究每一種編碼具體把某一個字符編碼成了哪幾個字節,我們隻需要知道“編碼”的概念就是把“字符”轉化成“字節”就可以了。
對於“UNICODE 編碼”,由於它們是可以通過計算得到的,因此,在特殊的場合,我們可以去了解某一種“UNICODE 編碼”是怎樣的規則。
2. 字符與編碼在程序中的實現
2.1 程序中的字符與字節
在 C++ 和 Java 中,用來代表“字符”和“字節”的數據類型,以及進行編碼的方法:
類型或操作 | C++ | Java |
字符 | wchar_t | char |
字節 | char | byte |
ANSI 字符串 | char[] | byte[] |
UNICODE 字符串 | wchar_t[] | String |
字節串→字符串 | mbstowcs(), MultiByteToWideChar() | string = new String(bytes, "encoding") |
字符串→字節串 | wcstombs(), WideCharToMultiByte() | bytes = string.getBytes("encoding") |
以上需要注意幾點:
- Java 中的 char 代表一個“UNICODE 字符(寬字節字符)”,而 C++ 中的 char 代表一個字節。
- MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函數。
2.2 C++ 中相關實現方法
聲明一段字符串常量:
// ANSI 字符串,內容長度 7 字節 char sz[20] = "中文123"; // UNICODE 字符串,內容長度 5 個 wchar_t(10 字節) wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";
UNICODE 字符串的 I/O 操作,字符與字節的轉換操作:
// 運行時設定當前 ANSI 編碼,VC 格式 setlocale(LC_ALL, ".936"); // GCC 中格式 setlocale(LC_ALL, "zh_CN.GBK"); // Visual C++ 中使用小寫 %s,按照 setlocale 指定編碼輸出到文件 // GCC 中使用大寫 %S fwprintf(fp, L"%s\n", wsz); // 把 UNICODE 字符串按照 setlocale 指定的編碼轉換成字節 wcstombs(sz, wsz, 20); // 把字節串按照 setlocale 指定的編碼轉換成 UNICODE 字符串 mbstowcs(wsz, sz, 20);
在 Visual C++ 中,UNICODE 字符串常量有更簡單的表示方法。如果源程序的編碼與當前默認 ANSI 編碼不符,則需要使用 #pragma setlocale,告訴編譯器源程序使用的編碼:
// 如果源程序的編碼與當前默認 ANSI 編碼不一致, // 則需要此行,編譯時用來指明當前源程序使用的編碼 #pragma setlocale(".936") // UNICODE 字符串常量,內容長度 10 字節 wchar_t wsz[20] = L"中文123";
以上需要注意 #pragma setlocale 與 setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在編譯時起作用,setlocale() 在運行時起作用。
2.3 Java 中相關實現方法
字符串類 String 中的內容是 UNICODE 字符串:
// Java 代碼,直接寫中文 String string = "中文123"; // 得到長度為 5,因為是 5 個字符 System.out.println(string.length());
字符串 I/O 操作,字符與字節轉換操作。在 Java 包 java.io.* 中,以“Stream”結尾的類一般是用來操作“字節串”的類,以“Reader”,“Writer”結尾的類一般是用來操作“字符串”的類。
// 字符串與字節串間相互轉化 // 按照 GB2312 得到字節(得到多字節字符串) byte [] bytes = string.getBytes("GB2312"); // 從字節按照 GB2312 得到 UNICODE 字符串 string = new String(bytes, "GB2312"); // 要將 String 按照某種編碼寫入文本文件,有兩種方法: // 第一種辦法:用 Stream 類寫入已經按照指定編碼轉化好的字節串 OutputStream os = new FileOutputStream("1.txt"); os.write(bytes); os.close(); // 第二種辦法:構造指定編碼的 Writer 來寫入字符串 Writer ow = new OutputStreamWriter(new FileOutputStream("2.txt"), "GB2312"); ow.write(string); ow.close(); /* 最後得到的 1.txt 和 2.txt 都是 7 個字節 */
如果 java 的源程序編碼與當前默認 ANSI 編碼不符,則在編譯的時候,需要指明一下源程序的編碼。比如:
E:\>javac -encoding BIG5 Hello.java
以上需要注意區分源程序的編碼與 I/O 操作的編碼,前者是在編譯時起作用,後者是在運行時起作用。
3. 幾種誤解,以及亂碼產生的原因和解決辦法
3.1 容易產生的誤解
對編碼的誤解 | |
誤解一 | 在將“字節串”轉化成“UNICODE 字符串”時,比如在讀取文本文件時,或者通過網絡傳輸文本時,容易將“字節串”簡單地作為單字節字符串,采用每“一個字節”就是“一個字符”的方法進行轉化。 而實際上,在非英文的環境中,應該將“字節串”作為 ANSI 字符串,采用適當的編碼來得到 UNICODE 字符串,有可能“多個字節”才能得到“一個字符”。 通常,一直在英文環境下做開發的程序員們,容易有這種誤解。 |
誤解二 | 在 DOS,Windows 98 等非 UNICODE 環境下,字符串都是以 ANSI 編碼的字節形式存在的。這種以字節形式存在的字符串,必須知道是哪種編碼才能被正確地使用。這使我們形成了一個慣性思維:“字符串的編碼”。 當 UNICODE 被支持後,Java 中的 String 是以字符的“序號”來存儲的,不是以“某種編碼的字節”來存儲的,因此已經不存在“字符串的編碼”這個概念了。隻有在“字符串”與“字節串”轉化時, 或者,將一個“字節串”當成一個 ANSI 字符串時,才有編碼的概念。不少的人都有這個誤解。 |
第一種誤解,往往是導致亂碼產生的原因。第二種誤解,往往導致本來容易糾正的亂碼問題變得更複雜。
在這裏,我們可以看到,其中所講的“誤解一”,即采用每“一個字節”就是“一個字符”的轉化方法,實際上也就等同於采用 iso-8859-1 進行轉化。因此,我們常常使用 bytes = string.getBytes("iso-8859-1") 來進行逆向操作,得到原始的“字節串”。然後再使用正確的 ANSI 編碼,比如 string = new String(bytes, "GB2312"),來得到正確的“UNICODE 字符串”。
3.2 非 UNICODE 程序在不同語言環境間移植時的亂碼
非 UNICODE 程序中的字符串,都是以某種 ANSI 編碼形式存在的。如果程序運行時的語言環境與開發時的語言環境不同,將會導致 ANSI 字符串的顯示失敗。
比如,在日文環境下開發的非 UNICODE 的日文程序界麵,拿到中文環境下運行時,界麵上將顯示亂碼。如果這個日文程序界麵改為采用 UNICODE 來記錄字符串,那麼當在中文環境下運行時,界麵上將可以顯示正常的日文。
由於客觀原因,有時候我們必須在中文操作係統下運行非 UNICODE 的日文軟件,這時我們可以采用一些工具,比如,南極星,AppLocale 等,暫時的模擬不同的語言環境。
4. 幾種錯誤理解的糾正
誤解:“ISO-8859-1 是國際編碼?”
非也。iso-8859-1 隻是單字節字符集中最簡單的一種,也就是“字節編號”與“UNICODE 字符編號”一致的那種編碼規則。
當我們要把一個“字節串”轉化成“字符串”,而又不知道它是哪一種 ANSI 編碼時,先暫時地把“每一個字節”作為“一個字符”進行轉化,不會造成信息丟失。
然後再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢複到原始的字節串。
誤解:“Java 中,怎樣知道某個字符串的內碼?”
Java 中,字符串類 java.lang.String 處理的是 UNICODE 字符串,不是 ANSI 字符串。
我們隻需要把字符串作為“抽象的符號的串”來看待。因此不存在字符串的內碼的問題。
https://swingalone.blog.sohu.com/40580793.html
ASCII碼, DBCS碼,SBCS碼 與Unicode碼
對於英文來說,ascii碼 0-127就足以代碼所有字符,對於中文而言,則必須使用兩個字節(byte)來代表一個字符,
具第一個字節必須大於127(所以我們有許程序判斷中文都是以ascii碼大於127作為條件)
以上用兩個字節來表示一個中文的方式,在習慣上稱為雙字節(即DBCS: Double-Byte Character Set),而相對之下,
英文的字符碼就稱為單字節SBCS(Single-Byte Character Set)。
雖然雙字節(DBCS)足以解決中英文字符混合使用情況,但對於不同字符係統而言,必須經過字符碼轉換,非常麻煩。例如:中英文混合情況,日文,韓文等等。
為解決這個問題,Apple, Xerox, Microsoft, IBM, Novell, Borland...很多公司聯合起來製訂了一套可以適用於全世界所有國家的字符碼,就稱為Unicode
java的內核就是16位雙字節編碼Unicode為基準
char類型表示Unicode編碼方案中的字符。
Unicode可同時包含65536個字符,ASCII/ANSI隻包含255個字符,實際上是Unicode的一個子集。
Unicode字符通常用十六進製編碼方案表示,範圍在'\u0000'到'\uFFFF'之間。\u0000到\u00FF表示ASCII/ANSI字符。\u表示這是一個Unicode值。
在Java中除了用這個\u的形式來表示字符外,還可以使用換碼序列來表示特殊字符。
Unicode是一種字符集,它有多種編碼方式UTF-32,UTF-16,UTF-8,UTF-7等等。一個Unicode字符一般占用兩個字節。ASCII(DBCS)是混合編碼的,0x00-0x7f是傳統的ASCII碼,如果一個字節的值是0x80-0xff表示,它與後麵的一個字節組成一個編碼單位。
目前計算機中用得最廣泛的字符集及其編碼,是由美國國家標準局(ANSI)製定的ASCII碼(American Standard Code for Information Interchange,美國標準信息交換碼),它已被國際標準化組織(ISO)定為國際標準,稱為ISO 646標準。適用於所有拉丁文字字母,ASCII碼有7位碼和8位碼兩種形式。
第0~32號及第127號(共34個)是控製字符或通訊專用字符,如控製符:LF(換行)、CR(回車)、FF(換頁)、DEL(刪除)、BEL(振鈴)等; 通訊專用字符:SOH(文頭)、EOT(文尾)、ACK(確認)等;
第33~126號(共94個)是字符,其中第48~57號為0~9十個阿拉伯數字;65~90號為26個大寫英文字母,97~122號為26個小寫英文字母,其餘為一些標點符號、運算符號等。
Unicode碼:Unicode碼也是一種國際標準編碼,采用二個字節編碼,與ANSI碼不兼容。目前,在網絡、Windows係統和很多大型軟件中得到應用。
Unicode(統一碼、萬國碼、單一碼)是一種在計算機上使用的字符編碼。它為每種語言中的每個字符設定了統一並且唯一的二進製編碼,以滿足跨語言、跨平台進行文本轉換、處理的要求。1990年開始研發,1994年正式公布。隨著計算機工作能力的增強,Unicode也在麵世以來的十多年裏得到普及。
Unicode 的編碼和實現
大概來說,Unicode 編碼係統可分為編碼方式和實現方式兩個層次。
1.編碼方式
Unicode 的編碼方式與 ISO 10646 的通用字元集(亦稱[通用字符集])(Universal Character Set,UCS)概念相對應,目前的用於實用的 Unicode 版本對應於 UCS-2,使用16位的編碼空間。也就是每個字符占用2個字節。這樣理論上一共最多可以表示 216 個字符。基本滿足各種語言的使用。實際上目前版本的 Unicode 尚未填充滿這16位編碼,保留了大量空間作為特殊使用或將來擴展。
上述16位 Unicode 字符構成基本多文種平麵(Basic Multilingual Plane, 簡稱 BMP)。最新(但未實際廣泛使用)的 Unicode 版本定義了16個輔助平麵,兩者合起來至少需要占據21位的編碼空間,比3字節略少。但事實上輔助平麵字符仍然占用4字節編碼空間,與 UCS-4 保持一致。未來版本會擴充到 ISO 10646-1 實現級別3,即涵蓋 UCS-4 的所有字符。UCS-4 是一個更大的尚未填充完全的31位字符集,加上恒為0的首位,共需占據32位,即4字節。理論上最多能表示
231 個字符,完全可以涵蓋一切語言所用的符號。
BMP 字符的 Unicode 編碼表示為 U+hhhh,其中每個 h 代表一個十六進製數位。與 UCS-2 編碼完全相同。對應的4字節 UCS-4 編碼後兩個字節一致,前兩個字節的所有位均為0。
2.實現方式
Unicode 的實現方式不同於編碼方式。一個字符的 Unicode 編碼是確定的。但是在實際傳輸過程中,由於不同係統平台的設計不一定一致,以及出於節省空間的目的,對 Unicode 編碼的實現方式有所不同。Unicode 的實現方式稱為Unicode轉換格式(Unicode Translation Format,簡稱為 UTF)。
例如,如果一個僅包含基本7位ASCII字符的 Unicode 文件,如果每個字符都使用2字節的原 Unicode 編碼傳輸,其第一字節的8位始終為0。這就造成了比較大的浪費。對於這種情況,可以使用 UTF-8 編碼,這是一種變長編碼,它將基本7位ASCII字符仍用7位編碼表示,占用一個字節(首位補0)。而遇到與其他 Unicode 字符混合的情況,將按一定算法轉換,每個字符使用1-3個字節編碼,並利用首位為0或1進行識別。這樣對以7位ASCII字符為主的西文文檔就大大節省了編碼長度(具體方案參見UTF-8)。類似的,對未來會出現的需要4個字節的輔助平麵字符和其他
UCS-4 擴充字符,2字節編碼的 UTF-16 也需要通過一定的算法進行轉換。
再如,如果直接使用與 Unicode 編碼一致(僅限於 BMP 字符)的 UTF-16 編碼,由於每個址 加昧肆礁鱟紙塚 贛acintosh機和PC機上對字節順序的理解是不一致的。這時同一字節流可能會被解釋為不同內容,如編碼為 U+594E 的字符“奎”同編碼為 U+4E59 的“乙”就可能發生混淆。於是在 UTF-16 編碼實現方式中使用了大尾序(big-endian)、小尾序(little-endian)的概念,以及BOM(Byte Order Mark)解決方案。(具體方案參見UTF-16)
此外 Unicode 的實現方式還包括 UTF-7、Punycode、CESU-8、SCSU、UTF-32等,這些實現方式有些僅在一定的國家和地區使用,有些則屬於未來的規劃方式。目前通用的實現方式是 UTF-16小尾序(BOM)、UTF-16大尾序(BOM)和 UTF-8。在微軟公司Windows XP操作係統附帶的記事本中,“另存為”對話框可以選擇的四種編碼方式除去非 Unicode 編碼的 ANSI 外,其餘三種“Unicode”、“Unicode big endian”和“UTF-8”即分別對應這三種實現方式。
目前輔助平麵的工作主要集中在第二和第三平麵的中日韓統一表意文字中,因此包括GBK、GB18030、Big5等簡體中文、正體中文、日文、韓語以及越南字喃的各種編碼與 Unicode 的協調性被重點關注。考慮到 Unicode 最終要涵蓋所有的字符,從某種意義而言,這些編碼方式也可視作 Unicode 的出現於其之前的既成事實的實現方式,如同ASCII及其擴展Latin-1一樣,後兩者的字符在16位 Unicode 編碼空間中的編碼第一字節各位全為0,第二字節編碼與原編碼完全一致。但上述東亞語言編碼與
Unicode 編碼的對應關係要複雜得多。
非 Unicode 環境
在非 Unicode 環境下,由於不同國家和地區采用的字符集不一致,很可能出現無法正常顯示所有字符的情況。微軟公司使用了代碼頁(Codepage)轉換表的技術來過渡性的部分解決這一問題,即通過指定的轉換表將非 Unicode 的字符編碼轉換為同一字符對應的係統內部使用的 Unicode 編碼。可以在“語言與區域設置”中選擇一個代碼頁作為非 Unicode 編碼所采用的默認編碼方式,如936為簡體中文GBK,950為正體中文Big5(皆指PC上使用的)。在這種情況下,一些非英語的歐洲語言編寫的軟件和文檔很可能出現亂碼。而將代碼頁設置為相應語言中文處理又會出現問題,這一情況無法避免。從根本上說,完全采用統一編碼才是解決之道,但目前上無法做到這一點。
代碼頁技術現在廣泛為各種平台所采用。UTF-7 的代碼頁是65000,UTF-8 的代碼頁是65001。
Unicode 目前已經有5.0版本。世界上有一大批計算機、語言學等科學家專門研究Unicode,到了現在Unicode標準已經不單是一個編碼標準,還是記錄人類語言文字資料的一個巨大的數據庫,同時從事人類文化遺產的發掘和保護工作。
對於中文而言,Unicode 16編碼裏麵已經包含了GB18030裏麵的所有漢字(27484個字),目前Unicode標準準備把康熙字典的所有漢字放入到Unicode 32bit編碼中。
簡單地說,Unicode擴展自ASCII字元集。在嚴格的ASCII中,每個字元用7位元表示,或者電腦上普遍使用的每字元有8位元寬;而Unicode使用全16位元字元集。這使得Unicode能夠表示世界上所有的書寫語言中可能用於電腦通訊的字元、象形文字和其他符號。Unicode最初打算作為ASCII的補充,可能的話,最終將代替它。考慮到ASCII是電腦中最具支配地位的標準,所以這的確是一個很高的目標。
Unicode影響到了電腦工業的每個部分,但也許會對作業係統和程式設計語言的影響最大。從這方麵來看,我們已經上路了。Windows NT從底層支援Unicode(不幸的是,Windows 98隻是小部分支援Unicode)。先天即被ANSI束縛的C程式設計語言通過對寬字元集的支援來支援Unicode。
字符串基礎 ASCII MBCS UNICODE DBCS
有的字符串類都起源於C語言的字符串,而C語言字符串則是字符的數組。首先了解一下字符類型。有三種編碼方式和三種字符類型。
第一種編碼方式是單字節字符集,稱之為SBCS,它的所有字符都隻有一個字節的長度。ASCII碼就是SBCS。SBCS字符串由一個零字節結尾。
第二種編碼方式是多字節字符集,稱之為MBCS,它包含的字符中有單字節長的字符,也有多字節長的字符。Windows用到的MBCS隻有二種字符類型,單字節字符和雙字節字符。
因此Windows中用得最多的字符是雙字節字符集,即DBCS,通常用它來代替MBCS。
在DBCS編碼中,用一些保留值來指明該字符屬於雙字節字符。例如,Shift-JIS(通用日語)編碼中,值0x81-0x9F 和 0xE0-0xFC 的意思是:
“這是一個雙字節字符,下一個字節是這個字符的一部分”。這樣的值通常稱為前導字節(lead byte),總是大於0x7F。
前導字節後麵是跟隨字節(trail byte)。DBCS的跟隨字節可以是任何非零值。與SBCS一樣,DBCS字符串也由一個零字節結尾。
第三種編碼方式是Unicode。 Unicode編碼標準中的所有字符都是雙字節長。
有時也將Unicode稱為寬字符集(wide characters),因為它的字符比單字節字符更寬(使用更多內存)。
注意,Unicode不是MBCS - 區別在於MBCS編碼中的字符長度是不同的。Unicode字符串用二個零字節字符結尾(一個寬字符的零值編碼)。
單字節字符集是拉丁字母,重音文字,用ASCII標準定義,用於DOS操作係統。雙字節字符集用於東亞和中東語言。Unicode用於COM和Windows NT內部。
讀者都很熟悉單字節字符集,它的數據類型是char。雙字節字符集也使用char數據類型(雙字節字符集中的許多古怪處之一)。
Unicode字符集用wchar_t數據類型。Unicode字符串用L前綴起頭,如:
wchar_t wch = L'1'; // 2 個字節, 0x0031 wchar_t* wsz = L"Hello"; // 12 個字節, 6 個寬字符
最後更新:2017-04-03 16:49:04