posts - 195, comments - 34, trackbacks - 0, articles - 1

          關(guān)于C++中文字符的處理

          Posted on 2010-04-18 01:10 小強摩羯座 閱讀(228) 評論(0)  編輯  收藏 所屬分類: C++ &VC

          關(guān)于C++中文字符的處理

          一 引入問題
          代碼 wchar_t a[3]=L”中國”,編譯時出錯,出錯信息為:數(shù)組越界。但wchar_t 是一個寬字節(jié)類型,數(shù)組a的大小應(yīng)為6個字節(jié),而兩個漢字的的unicode碼占4個字節(jié),再加上一個結(jié)束符,最多6個字節(jié),所以應(yīng)該不會越界。難道是編譯器出問題了?
          二 解決引入問題所需的知識
             主要需兩方面的知識,第一個為字符尤其是漢字的編碼,以及語言和工具的支持情況,第二個是vc/c++中MutiByte Charater Set 和 Wide Character Set有關(guān)內(nèi)存分配的情況.
          三 漢字的編碼方式及在vc/c++中的處理
          1.漢字編碼方式的介紹
          對英文字符的處理,7位ASCII碼字符集中的字符即可滿足使用需求,且英文字符在計算機上的輸入及輸出也非常簡單,因此,英文字符的輸入、存儲、內(nèi)部處理和輸出都可以只用同一個編碼(如ASCII碼)。
          而漢字是一種象形文字,字?jǐn)?shù)極多(現(xiàn)代漢字中僅常用字就有六、七千個,總字?jǐn)?shù)高達(dá)5萬個以上),且字形復(fù)雜,每一個漢字都有"音、形、義"三要素,同音字、異體字也很多,這些都給漢字的的計算機處理帶來了很大的困難。要在計算機中處理漢字,必須解決以下幾個問題:首先是漢字的輸入,即如何把結(jié)構(gòu)復(fù)雜的方塊漢字輸入到計算機中去,這是漢字處理的關(guān)鍵;其次,漢字在計算機內(nèi)如何表示和存儲?如何與西文兼容?最后,如何將漢字的處理結(jié)果從計算機內(nèi)輸出?
          為此,必須將漢字代碼化,即對漢字進行編碼。對應(yīng)于上述漢字處理過程中的輸入、內(nèi)部處理及輸出這三個主要環(huán)節(jié),每一個漢字的編碼都包括輸入碼、交換碼、內(nèi)部碼和字形碼。在計算機的漢字信息處理系統(tǒng)中,處理漢字時要進行如下的代碼轉(zhuǎn)換:輸入碼→交換碼→內(nèi)部碼→字形碼。
          (1)輸入碼: 作用是,利用它和現(xiàn)有的標(biāo)準(zhǔn)西文鍵盤結(jié)合來輸入漢字。輸入碼也稱為外碼。主要歸為四類:
          a)      數(shù)字編碼:數(shù)字編碼是用等長的數(shù)字串為漢字逐一編號,以這個編號作為漢字的輸入碼。例如,區(qū)位碼、電報碼等都屬于數(shù)字編碼。
          b)      拼音碼:拼音碼是以漢字的讀音為基礎(chǔ)的輸入辦法。
          c)      字形碼:字形碼是以漢字的字形結(jié)構(gòu)為基礎(chǔ)的輸入編碼。例如,五筆字型碼(王碼)。
          d)      音形碼:音形碼是兼顧漢字的讀音和字形的輸入編碼。
          (2)交換碼:用于漢字外碼和內(nèi)部碼的交換。交換碼的國家標(biāo)準(zhǔn)代號為GB2312-80。
          (3)內(nèi)部碼:內(nèi)部碼是漢字在計算機內(nèi)的基本表示形式,是計算機對漢字進行識別、存儲、處理和傳輸所用的編碼。內(nèi)部碼也是雙字節(jié)編碼,將國標(biāo)碼兩個字節(jié)的最高位都置為"1",即轉(zhuǎn)換成漢字的內(nèi)部碼。
          (4)字形碼:字形碼是表示漢字字形信息(漢字的結(jié)構(gòu)、形狀、筆劃等)的編碼,用來實現(xiàn)計算機對漢字的輸出(顯示、打印)。
          2.VC中漢字的編碼方式
               vc/c++正是采用了GB2312內(nèi)部碼作為漢字的編碼方式,因此vc/c++中的各種輸入輸出方法,如cin/wcin,cout/wcout,scanf/wsanf,printf/wprintf...都是基于GB2312的,如果漢字的內(nèi)碼不是這種編碼方式,那么利用上述各種方法就不會正確的解析漢字。
          仔細(xì)觀察ASCII字符表,從第161個字符開始,后面的字符并不經(jīng)常為用戶所使用,負(fù)值也未使用。GB2312編碼方式充分利用這一特性,將161-255(-95~-1)之間的數(shù)值空間作為漢字的標(biāo)識碼。既然255-161 = 94不能滿足漢字容量的要求,就將每兩個字符并在一塊(即一個漢字占兩個字節(jié)),顯然,94* 94 =8836基本上已經(jīng)滿足了常用漢字個數(shù)的要求。計算機處理字符時,當(dāng)連續(xù)處理到兩個大與160(或-95~-1)的字節(jié)時,就認(rèn)為這兩個字節(jié)存放了一個漢字字符。可以用下面的Demo程序來模擬vc/c++中輸出漢字字符的過程。
              unsigned char input[50];
          cin>>input;
              int flag=0;
               for(int i =0 ;i < 50 ;i++)
                {
                   if(input[i] > 0xa0 && input[i] != 0)
                    {
                        if(flag == 1)
                         {
                              cout<<"chinese character"<<endl;
                              flag = 0;
                         }
                        else
                         {
                              flag++;
                         }
                    }
                   else if(input[i] == 0)
                    {
                        break;
                    }
                   else
                    {
                         cout<<"english character"<<endl;
                    }
          }
          輸入:Hello中國 (“中國”對應(yīng)的GB2312內(nèi)碼為:214 208,185 250)
          輸出:english character
          english character
          english character
          english character
          english character
          chinese character
          chinese character
          vc/c++中的英文字符仍然采用ASCII編碼方式。可以設(shè)想,其他國家程序員利用vc/c++編寫程序輸入本國字符時,vc/c++則會采用該國的字符編碼方式來處理這些字符。
              問題又產(chǎn)生了,韓國的vc/c++程序在中國的vc/c++上運行時,如果沒有相應(yīng)的內(nèi)碼庫,則對韓語字符的顯示有可能出現(xiàn)亂碼。我個人猜測,vc安裝程序中應(yīng)該帶有不同國家的內(nèi)碼庫,這樣一來肯定會占用很大的空間。如果所有的國家使用統(tǒng)一的編碼方式,且所有的程序設(shè)計語言和開發(fā)工具都支持這種編碼方式該多好!而現(xiàn)實中,確實已經(jīng)有這種編碼方式了,且許多新的語言也都支持這種編碼方式,如Java、C#等,它就是下面的Unicode編碼
          3.新的內(nèi)碼標(biāo)準(zhǔn)---Unicode
          Unicode(統(tǒng)一碼、萬國碼、單一碼)是一種在計算機上使用的字符編碼。它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉(zhuǎn)換、處理的要求。1990年開始研發(fā),1994年正式公布。隨著計算機工作能力的增強,Unicode也在面世以來的十多年里得到普及。最新版本的 Unicode 是 2005年3月31推出的Unicode 4.1.0 。另外,5.0 Beta已于2005年12月12日推出,以供各會員評價。
          Unicode 編碼系統(tǒng)可分為編碼方式和實現(xiàn)方式兩個層次。
          編碼方式:Unicode 的編碼方式與 ISO 10646 的通用字符集(Universal Character Set,UCS)概念相對應(yīng),目前的用于實用的 Unicode 版本對應(yīng)于 UCS-2,使用16位的編碼空間。也就是每個字符占用2個字節(jié)。這樣理論上一共最多可以表示 216 個字符。基本滿足各種語言的使用。實際上目前版本的 Unicode 尚未填充滿這16位編碼,保留了大量空間作為特殊使用或?qū)頂U展。
          實現(xiàn)方式:Unicode 的實現(xiàn)方式不同于編碼方式。一個字符的 Unicode 編碼是確定的。但是在實際傳輸過程中,由于不同系統(tǒng)平臺的設(shè)計不一定一致,以及出于節(jié)省空間的目的,對 Unicode 編碼的實現(xiàn)方式有所不同。Unicode 的實現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Translation Format,簡稱為 UTF)。如,UTF-8 編碼,這是一種變長編碼,它將基本7位ASCII字符仍用7位編碼表示,占用一個字節(jié)(首位補0)。而遇到與其他 Unicode 字符混合的情況,將按一定算法轉(zhuǎn)換,每個字符使用1-3個字節(jié)編碼,并利用首位為0或1進行識別。
          Java與C#語言都是采用Unicode編碼方式,在這兩種語言中定義一個字符,在內(nèi)存中存放的就是這個字符的兩字節(jié)Unicode碼。如下所示:
          char a='我';     => 內(nèi)存中存放的Unicode碼為:25105
          4.內(nèi)碼的相互轉(zhuǎn)換
          (1)vc中的實現(xiàn)方法
             利用Windows系統(tǒng)提供的API:::MultiByteToWideChar::WideCharToMultiByte
          ::MultiByteToWideChar實現(xiàn)當(dāng)前碼到Unicode碼的轉(zhuǎn)換;
          ::WideCharToMultiByte實現(xiàn)Unicode碼到當(dāng)前碼的轉(zhuǎn)換;
          (2)Java中的實現(xiàn)方法
              String vcString=new String(javaString.getBytes("UTF-8"),"gb2312");
          java的編碼應(yīng)該是UTF-8
          (3)C#中的實現(xiàn)方法
              ??
          四 vc中的MutiByte Charater Set 和 Wide Character Set
          1.MultiByte Charater Set方式
             這種方式以按字節(jié)為單位存放字符,即如果一個字符碼為兩字節(jié),則在內(nèi)存中占兩字節(jié),字符碼為一字節(jié),就占一字節(jié)。例如,字符串“中國abc”的編碼為:中(0xd6、0xd0)、國(0xb9、0xfa)、a(0x61)、b(0x62)、c(0x63)、\0(0x00),就存為如下方式:
          對應(yīng)的類型,方法有:
          char、scanf、printf、cin、cout …
          2.Wide Character Set
          這種方式是以兩字節(jié)為單位存放字符,即如果一個字符碼為兩字節(jié),則在內(nèi)存中占四字節(jié),字符碼為一字節(jié),就占兩字節(jié)。例如,字符串“中國abc”就存為如下方式:
          對應(yīng)的類型,方法有:
          wchar_t、wscanf、wprintf、wcin、wcout …
          造成上面存儲方式的根本原因在于,wchar_t類型其實是一個unsigned short 類型。如,存儲上面字符串的數(shù)組的定義為:wchar_t buffer[8] 等價于unsigned short buffer[8].而所有以字母w開頭的方法也都是以unsigned short類型,即兩字節(jié)為單位來處理字符,因此,存儲在wchar_t類型數(shù)組中的字符串無法用cout顯示,只能用wcout方法來顯示。
          由于Unicode碼也是采用兩個字節(jié),因此Wide Character Set方式能夠很好的支持Unicode碼的存儲,但是在vc的環(huán)境下要將一個Unicode碼存入兩字節(jié)而不是四字節(jié)內(nèi)存中,必須通過上面的API函數(shù)::MultiByteToWideChar首先,將當(dāng)前的編碼轉(zhuǎn)換為Unicode碼,然后,將每個字符的Unicode碼放入每一個wchar_t類型的變量中。以下是一個實例代碼:
          char input[50];
          cin>>input;
          int size;
          size=::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,NULL,0);
          if(size==0)
               return -1;
          wchar_t *widebuff=new wchar_t[size];
          ::MultiByteToWideChar(CP_ACP,0,input,strlen(input)+1,widebuff,size);
          輸入:中國abc
          Debug斷點調(diào)試:
          size==6
          數(shù)組widebuff[0-size]占12字節(jié),存放了6個字符的Unicode碼,碼值為:
          中(0x4e2d) 國(0x56fd) a(0x0061) b(0x0062) c(0x0063) d(0x0000)
          這時,數(shù)組的大小size等于輸入的字符個數(shù)加上一個結(jié)束符,符合我們的想象。
          五 引入問題的錯誤分析
          (1) 沒有理解編譯器中的編碼方式
              雖然vc/c++中漢字的編碼占兩個字節(jié),但并不是Unicode碼,是GB2312碼。
          (2) 沒有理解MutiByte Charater Set 和 Wide Character Set的存儲原則;
              在vc/c++中,“中國”按char[5]來對待,而wchar_t a[3]實際上是三個unsigned short類型的變量,因此賦值時會越界。


          主站蜘蛛池模板: 黑龙江省| 刚察县| 岗巴县| 灵寿县| 盘山县| 奇台县| 鄂托克旗| 班玛县| 峨山| 山西省| 黑龙江省| 襄城县| 瑞金市| 临沂市| 瑞安市| 蒙城县| 黄梅县| 云霄县| 青岛市| 准格尔旗| 通化市| 施甸县| 巩义市| 阳原县| 思茅市| 济宁市| 广宗县| 凤冈县| 宝清县| 弥渡县| 邹城市| 色达县| 顺平县| 凤台县| 昭平县| 康马县| 临沧市| 遵义市| 富平县| 永寿县| 榕江县|