1. 程式人生 > >將漢字儲存在C語言字元中

將漢字儲存在C語言字元中

其實在C語言中可以儲存漢字,但是這種方式不作為推薦方法使用,這裡只是介紹一個小技巧。

#include<stdio.h>

void main()

{

char x[] ="你好";

char y[] ="天氣";

char t[] ="";

char *p[] = {x,y,t};

printf("%s",p[2]);

}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

昨天去面試,面試官更深入的問了一些問題,面試官的解答讓我更深入的去思考這個問題。我現在Mac上寫了一個簡單地程式,試了一下,程式碼如下:

int main(int argc, const char * argv[]) {
    char p[] = "漢子";
    printf("%s", p);
    return 0;
}
然後我通過設定斷點得知儲存這兩個漢字一共佔據了7個位元組,其中每個漢字佔據了3個位元組,最後一個位元組用來儲存字串結束符\0。通過昨天面試官的提示,我查閱了一些關於字元編碼的資料,得知Mac是採用UTF-8的編碼方式。
下面來介紹一下漢字的儲存方式,關於漢字的儲存方式要根據具體的字元編碼和作業系統決定,分別來介紹一下:

GB2312字符集及編碼方式:

計算機剛開始發展起來的時候,只是可以顯示英文字母,但是後來為滿足天朝人們英語不好的需求,天朝的專家們自己設計出了一套滿足中文編碼方式的規則,這樣GB2312就誕生了。GB2312編碼規則是將漢字轉化成計算機可以接受的數字的統一的規則。它規定:一個小於127的位元組表示的字元意義與原來相同,但是兩個大於127的位元組連在一起的時候就可以表示漢字,前面的一個位元組(高位元組)用從0xA1到0xF7來表示,後面一個位元組(低位元組)用從0xA1到0xFE來表示。其實這有些不懂,不過我通過百度查到了一些資料如下

GB 2312中對所收漢字進行了“分割槽”處理,每區含有94個漢字/符號。這種表示方式也稱為區位碼

01-09區為特殊符號。

16-55區為一級漢字,按拼音排序。

56-87區為二級漢字,按部首/筆畫排序。

10-15區及88-94區則未有編碼。

每個漢字及符號以兩個位元組來表示。第一個位元組稱為“高位位元組”(也稱“區位元組)”,第二個位元組稱為“低位位元組”(也稱“位位元組”)。“高位位元組”使用了0xA1-0xF7(把01-87區的區號加上0xA0),“低位位元組”使用了0xA1-0xFE(把01-94加上 0xA0)。 由於一級漢字從16區起始,漢字區的“高位位元組”的範圍是0xB0-0xF7,“低位位元組”的範圍是0xA1-0xFE,佔用的碼位是 72*94=6768。其中有5個空位是D7FA-D7FE。

舉例來說,“啊”字是GB2312之中的第一個漢字,它的區位碼就是1601。“啊”字在大多數程式中,會以兩個位元組,0xB0(第一個位元組) 0xA1(第二個位元組)儲存。區位碼=區位元組+位位元組(與區位碼對比:0xB0=0xA0+16,0xA1=0xA0+1)。

這一下我就明白了怎麼用GB2312進行相應的計算及轉化。

Unicode字符集及編碼方式:

為了滿足全世界各種語言可以向英語那樣在電腦螢幕上顯示出來,Unicode提供了4位元組為所有語言中的字元進行統一的編碼,就相當與為全世界提供了一個標準,讓全世界人來使用共同的標準來達到一個統一的結果。

UTF-32:

用4個位元組的數字來表示每個字母,符號或者表意文字,每四個位元組的數字表示唯一的至少在某種語言中使用的符號的編碼方案稱為UTF-32.

UTF-16:

儘管有Unicode字元非常多,但是實際上大多數人不會用到超過前65535個以外的字元。因此,就有了另外一種Unicode編碼方式,叫做UTF-16(因為16位 = 2位元組)。UTF-16將0–65535範圍內的字元編碼成2個位元組,如果真的需要表達那些很少使用的"星芒層(astral plane)"內超過這65535範圍的Unicode字元,則需要使用一些詭異的技巧來實現。UTF-16編碼最明顯的優點是它在空間效率上比UTF-32高兩倍,因為每個字元只需要2個位元組來儲存(除去65535範圍以外的),而不是UTF-32中的4個位元組。並且,如果我們假設某個字串不包含任何星芒層中的字元,那麼我們依然可以在常數時間內找到其中的第N個字元,直到它不成立為止這總是一個不錯的推斷。其編碼方法是:

如果字元編碼U小於0x10000,也就是十進位制的0到65535之內,則直接使用兩位元組表示;

如果字元編碼U大於0x10000,由於UNICODE編碼範圍最大為0x10FFFF,從0x10000到0x10FFFF之間 共有0xFFFFF個編碼,也就是需要20個bit就可以標示這些編碼。用U'表示從0-0xFFFFF之間的值,將其前 10 bit作為高位和16 bit的數值0xD800進行 邏輯or 操作,將後10 bit作為低位和0xDC00做 邏輯or 操作,這樣組成的 4個byte就構成了U的編碼。

對於UTF-32和UTF-16編碼方式還有一些其他不明顯的缺點。不同的計算機系統會以不同的順序儲存位元組。這意味著字元U+4E2D在UTF-16編碼方式下可能被儲存為4E 2D或者2D 4E,這取決於該系統使用的是大尾端(big-endian)還是小尾端(little-endian)。(對於UTF-32編碼方式,則有更多種可能的位元組排列。)只要文件沒有離開你的計算機,它還是安全的——同一臺電腦上的不同程式使用相同的位元組順序(byte order)。但是當我們需要在系統之間傳輸這個文件的時候,也許在全球資訊網中,我們就需要一種方法來指示當前我們的位元組是怎樣儲存的。不然的話,接收文件的計算機就無法知道這兩個位元組4E 2D表達的到底是U+4E2D還是U+2D4E。

為了解決這個問題,多位元組的Unicode編碼方式定義了一個"位元組順序標記(Byte Order Mark)",它是一個特殊的非列印字元,你可以把它包含在文件的開頭來指示你所使用的位元組順序。對於UTF-16,位元組順序標記是U+FEFF。如果收到一個以位元組FF FE開頭的UTF-16編碼的文件,你就能確定它的位元組順序是單向的(one way)的了;如果它以FE FF開頭,則可以確定位元組順序反向了。

UTF-8:

UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼定長碼),也是一種字首碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII相容,這使得原來處理ASCII字元的軟體無須或只須做少部份修改,即可繼續使用。因此,它逐漸成為電子郵件網頁及其他儲存或傳送文字的應用中,優先採用的編碼。網際網路工程工作小組(IETF)要求所有網際網路協議都必須支援UTF-8編碼。

UTF-8使用一至四個位元組為每個字元編碼:

128個US-ASCII字元只需一個位元組編碼(Unicode範圍由U+0000至U+007F)。

帶有附加符號拉丁文希臘文西裡爾字母亞美尼亞語希伯來文阿拉伯文敘利亞文它拿字母則需要二個位元組編碼(Unicode範圍由U+0080至U+07FF)。

其他基本多文種平面(BMP)中的字元(這包含了大部分常用字)使用三個位元組編碼。

其他極少使用的Unicode輔助平面的字元使用四位元組編碼

在處理經常會用到的ASCII字元方面非常有效。在處理擴充套件的拉丁字符集方面也不比UTF-16差。對於中文字元來說,比UTF-32要好。同時,(在這一條上你得相信我,因為我不打算給你展示它的數學原理。)由位操作的天性使然,使用UTF-8不再存在位元組順序的問題了。一份以utf-8編碼的文件在不同的計算機之間是一樣的位元流。

總體來說,在Unicode字串中不可能由碼點數量決定顯示它所需要的長度,或者顯示字串之後在文字緩衝區中游標應該放置的位置;組合字元、變寬字型、不可列印字元和從右至左的文字都是其歸因。所以儘管在UTF-8字串中字元數量與碼點數量的關係比UTF-32更為複雜,在實際中很少會遇到有不同的情形。

優點

UTF-8是ASCII的一個超集。因為一個純ASCII字串也是一個合法的UTF-8字串,所以現存的ASCII文字不需要轉換。為傳統的擴充套件ASCII字符集設計的軟體通常可以不經修改或很少修改就能與UTF-8一起使用。

使用標準的面向位元組的排序例程對UTF-8排序將產生與基於Unicode程式碼點排序相同的結果。(儘管這隻有有限的有用性,因為在任何特定語言或文化下都不太可能有仍可接受的文字排列順序。)

UTF-8和UTF-16都是可擴充套件標記語言文件的標準編碼。所有其它編碼都必須通過顯式或文字宣告來指定。

任何面向位元組字串搜尋演算法都可以用於UTF-8的資料(只要輸入僅由完整的UTF-8字元組成)。但是,對於包含字元記數的正則表示式或其它結構必須小心。

UTF-8字串可以由一個簡單的演算法可靠地識別出來。就是,一個字串在任何其它編碼中表現為合法的UTF-8的可能性很低,並隨字串長度增長而減小。舉例說,字元值C0,C1,F5至FF從來沒有出現。為了更好的可靠性,可以使用正則表示式來統計非法過長和替代值(可以檢視W3 FAQ: Multilingual Forms上的驗證UTF-8字串的正則表示式)。

缺點

因為每個字元使用不同數量的位元組編碼,所以尋找串中第N個字元是一個O(N)複雜度的操作 — 即,串越長,則需要更多的時間來定位特定的字元。同時,還需要位變換來把字元編碼成位元組,把位元組解碼成字元