音影先锋亚洲天堂网|电影世界尽头的爱完整版播放|国产 熟女 91|高清无码免费观看欧美日韩|韩国一区二区三区黄色录像|美女亚洲加勒比在线|亚洲综合网 开心五月|7x成人在线入口|成人网站免费日韩毛片区|国产黄片?一级?二级?三级

C/C++ 編程中多國(guó)語(yǔ)言處理

出處:jinzhao 發(fā)布于:2010-04-13 14:42:47

  摘要:多國(guó)語(yǔ)言的存在,使程序員在編碼處理上花費(fèi)了大量時(shí)間和精力;然而各種各樣的亂碼問(wèn)題,如 XML 格式錯(cuò)誤、文本顯示異常、解析器異常等依然層出不窮。特別的,相對(duì)于 JAVA 語(yǔ)言,C/C++ 在處理編碼問(wèn)題上有更大的困難。本文避免糾纏不同編碼格式的具體異同,以 Unicode 為,以簡(jiǎn)體中文為例,從工程應(yīng)用角度分析編碼問(wèn)題存在的原因,不僅提出 C/C++ 標(biāo)準(zhǔn)庫(kù)編程的解決方案,更結(jié)合項(xiàng)目經(jīng)驗(yàn),總結(jié)出處理多國(guó)語(yǔ)言編碼問(wèn)題的一般思路。

  問(wèn)題的提出

  多國(guó)語(yǔ)言的存在、不同語(yǔ)言操作系統(tǒng)的存在,使得針對(duì)多語(yǔ)言的設(shè)計(jì)頗費(fèi)周章,在編碼上所付出的工作量也是可觀的。所謂編碼的問(wèn)題,歸結(jié)起來(lái),就是二進(jìn)制的編碼以何種編碼格式進(jìn)行解析的問(wèn)題。特別是在硬盤文件和內(nèi)存數(shù)據(jù)的相互轉(zhuǎn)化、即讀寫過(guò)程中,如果采用了錯(cuò)誤的編碼格式,就會(huì)造成亂碼。JAVA 語(yǔ)言在字符串、編碼等處理方面給了程序員更為直接、方便的接口,習(xí)慣使用 JAVA 做編碼的程序員,在使用 C/C++ 進(jìn)行文本編碼相關(guān)的操作時(shí),常會(huì)感到困惑。本文的目的在于以常用的 Unicode(UCS-2)、GB2312、UTF8 三種編碼為例,分析不同編碼在實(shí)用中的關(guān)系,特別是 C/C++ 中,怎樣處理各種編碼的問(wèn)題。

  編碼處理常見的問(wèn)題

  1. 將內(nèi)存中編碼 A 的字符串以編碼 B 格式處理成字節(jié)流寫入文件

  2. 將原本以 A 編碼組成的文件以字節(jié)流形式讀入內(nèi)存、并以編碼 B 解析為字符串。

  種情況,可能造成數(shù)據(jù)的變化、失真。

  如果使用 JAVA 語(yǔ)言,發(fā)生這種錯(cuò)誤的情況稍少一些,因?yàn)樵?JAVA 中沒有 wstring 這種概念,在內(nèi)存中的 String,使用的編碼都是 Unicode,其中的轉(zhuǎn)換對(duì)于程序員來(lái)講是透明的。只要使用輸入 / 輸出方法時(shí)注意字節(jié)流的字符集選擇即可。

  例如,編碼為中文 GB2312 的“標(biāo)準(zhǔn)”字符串被讀入內(nèi)存后轉(zhuǎn)存為 UTF8 的過(guò)程:

  圖 1. 文件轉(zhuǎn)換編碼的 JAVA 處理方式

  但 C/C++ 編程,由于通常使用 char、string 類型的時(shí)候比較多,特別是進(jìn)行文件讀寫,基本都是操作 char* 類型的數(shù)據(jù)。并且也沒有像 JAVA 中 getByte(String charsetname) 這種函數(shù),不能直接根據(jù)字符集重新編碼得到字符串的 byte 數(shù)組。這時(shí)候,我們使用的 string 其實(shí)就一般不是 Unicode,而是符合某種編碼表的。這使得我們往往困惑于 string 的編碼問(wèn)題。假設(shè)有 utf8 的字符串“一”(E4 B8 80),而我們錯(cuò)誤的認(rèn)為它是符合 gb2312(編碼 A)的,并將其轉(zhuǎn)換為 utf8(編碼 B),這種轉(zhuǎn)換結(jié)果是破壞性的,錯(cuò)誤的輸出將永遠(yuǎn)無(wú)法正確識(shí)別。

  依然以“標(biāo)準(zhǔn)”為例,這是一個(gè)正確的轉(zhuǎn)換:

  圖 2. 文件轉(zhuǎn)換編碼的 C/C++ 處理方式

  第二種情況,則是更常見到的。例如:瀏覽器瀏覽網(wǎng)頁(yè)時(shí)的發(fā)生的亂碼問(wèn)題;在寫 XML 文件時(shí),指定了 < ?xml version="1.0" encoding="utf-8" ?> 然而文件中卻包含 GB2312 的字符串——這樣經(jīng)常會(huì)導(dǎo)致 XML 文件 bad formatted,而使得解析器出錯(cuò)。

  這種情況下,其實(shí)數(shù)據(jù)都是正確的,只要瀏覽器選擇正確的編碼,將 XML 文件中的 GB2312 轉(zhuǎn)換為 UTF8 或者修改 encoding,就可以解決問(wèn)題。

  需要注意的是,ASCII 碼的字符,即單字節(jié)字符,一般不受編碼變動(dòng)影響,在所有編碼表中的值是一樣的;需要小心處理的是多字節(jié)字符,例如中文語(yǔ)言。

  編碼轉(zhuǎn)換方法

  一般的編碼轉(zhuǎn)換,直接做映射的不太可能,需要比較多的工作量,大多情況下還是選擇 Unicode 作為轉(zhuǎn)換的中介。

  使用庫(kù)函數(shù)

  如前文所說(shuō),JAVA 的 String 對(duì)象是以 Unicode 編碼存在的,所以 JAVA 程序員主要關(guān)心的是讀入時(shí)判斷字節(jié)流的編碼,從而確??梢哉_的轉(zhuǎn)化為 Unicode 編碼;相比之下,C/C++ 將外部文件讀出的數(shù)據(jù)存為字符數(shù)組、或者是 string 類型;而 wstring 才是符合 Unicode 編碼的雙字節(jié)數(shù)組。一般常用的方法是 C 標(biāo)準(zhǔn)庫(kù)的 wcstombs、mbstowcs 函數(shù),和 windows API 的 MultiByteToWideChar 與 WideCharToMultiByte 函數(shù)來(lái)完成向 Unicode 的轉(zhuǎn)入和轉(zhuǎn)出。

  這里以 MBs2WCs 函數(shù)的實(shí)現(xiàn)說(shuō)明 GB2312 向 Unicode 的轉(zhuǎn)換的主要過(guò)程:

  清單 1. 多字節(jié)字符串向?qū)捵止?jié)字符串轉(zhuǎn)換

  wchar_t * MBs2WCs(const char* pszSrc){

  wchar_t* pwcs = NULL;

  intsize = 0;

  #ifdefined(_linux_)

  setlocale(LC_ALL, "zh_CN.GB2312");

  size = mbstowcs(NULL,pszSrc,0);

  pwcs = new wchar_t[size+1];

  size = mbstowcs(pwcs, pszSrc, size+1);

  pwcs[size] = 0;

  #else

  size = MultiByteToWideChar(20936, 0, pszSrc, -1, 0, 0);

  if(size <= 0)

  returnNULL;

  pwcs = new wchar_t[size];

  MultiByteToWideChar(20936, 0, pszSrc, -1, pwcs, size);

  #endif

  returnpwcs;

  }

  相應(yīng)的,WCs2MBs 可以將寬字符串轉(zhuǎn)化為字節(jié)流。

  清單 2. 寬字節(jié)字符串向多字節(jié)字符串轉(zhuǎn)換

  char* WCs2MBs(const wchar_t * wcharStr){

  char* str = NULL;

  intsize = 0;

  #ifdefined(_linux_)

  setlocale(LC_ALL, "zh_CN.UTF8");

  size = wcstombs( NULL, wcharStr, 0);

  str = new char[size + 1];

  wcstombs( str, wcharStr, size);

  str[size] = '\0';

  #else

  size = WideCharToMultiByte( CP_UTF8, 0, wcharStr, -1, NULL, NULL, NULL, NULL );

  str = new char[size];

  WideCharToMultiByte( CP_UTF8, 0, wcharStr, -1, str, size, NULL, NULL );

  #endif

  returnstr;

  }

  Linux 的 setlocale 的具體使用可以參閱有 C/C++ 文檔,它關(guān)系到文字、貨幣單位、時(shí)間等很多格式問(wèn)題。Windows 相關(guān)的代碼中 20936 和宏定義 CP_UTF8 是 GB2312 編碼對(duì)應(yīng)的的 Code Page[ 類似的 Code Page 參數(shù)可以從 MSDN的 Encoding Class 有關(guān)信息中獲得 ]。

  這里需要特別指出的是 setlocale 的第二個(gè)參數(shù),Linux 和 Windows 是不同的:

  1. 筆者在 Eclipse CDT + MinGW 下使用 [country].[charset](如 zh_CN.gb2312 或 zh_CN.UTF8)的格式并不能通過(guò)編碼轉(zhuǎn)換測(cè)試,但可以使用 Code Page,即可以寫成 setlocale(LC_ALL, ".20936") 這樣的代碼。這說(shuō)明,這個(gè)參數(shù)與編譯器無(wú)關(guān),而與系統(tǒng)定義有關(guān),而不同操作系統(tǒng)對(duì)于已安裝字符集的定義是不同的。

  2. Linux 系統(tǒng)下可以參見 /usr/lib/locale/ 路徑,系統(tǒng)所支持的 locale 都在這里。轉(zhuǎn)換成 UTF8 時(shí),并不需要 [country] 部分一定是 zh_CN,en_US.UTF8 也可以正常轉(zhuǎn)換。

  另外,標(biāo)準(zhǔn) C 和 Win32 API 函數(shù)返回值是不同的,標(biāo)準(zhǔn) C 返回的 wchar_t 數(shù)組或者是 char 數(shù)組都沒有字符串結(jié)束符,需要手動(dòng)賦值,所以 Linux 部分的代碼要有區(qū)別對(duì)待。

  ,還要注意應(yīng)當(dāng)在調(diào)用這兩個(gè)函數(shù)后釋放分配的空間。如果將 MBs2WCs 和 WCs2MBs 的返回值分別轉(zhuǎn)化為 wstring 和 string,就可以在它們函數(shù)體內(nèi)做 delete,這里為了代碼簡(jiǎn)明,故而省略,但請(qǐng)讀者別忘記。

  第三方庫(kù)

  目前的第三方工具已經(jīng)比較完善,這里介紹兩個(gè),本文側(cè)重點(diǎn)不在此,不對(duì)其做太多探討。

  Linux 上存在第三方的 iconv 項(xiàng)目,使用也較為簡(jiǎn)單,其實(shí)質(zhì)也是以 Unicode 作為轉(zhuǎn)換的中介。

  ICU 是一個(gè)很完善的國(guó)際化工具。其中的 Code Page Conversion 功能也可以支持文本數(shù)據(jù)從任何字符集向 Unicode 的雙向轉(zhuǎn)換。

  實(shí)驗(yàn)測(cè)試

  在代碼中調(diào)用“編碼轉(zhuǎn)換方法”一節(jié)里提到的函數(shù),將 gb2312 編碼的字符串轉(zhuǎn)換為 UTF8 編碼,分析其編碼轉(zhuǎn)換的行為:

  在英文 Linux 環(huán)境下,執(zhí)行下列命令:

  export LC_ALL=zh_CN.gb2312

  然后編譯并執(zhí)行以下程序(其中漢字都是在 gb2312 環(huán)境中寫入源文件)

  L1:   wstring ws = L"一";

  L2:   string s_gb2312 = "一";

  L3:   wchar_t * wcs = MBs2WChar(s_gb2312.c_str());

  L4:   char* cs = WChar2MBs(wcs);

  查看輸出:

  L1 - 1 wide char: 0x04BB

  L2 - 2 bytes:0xd2,0xbb,即 gb2312 編碼 0xD2BB

  L3 - 返回的 wchar_t 數(shù)組內(nèi)容為 0x4E00,也就是 Unicode 編碼

  L4 - 將 Unicode 再度轉(zhuǎn)換為 UTF8 編碼,輸出的字符長(zhǎng)度為 3,即 0xE4,oxB8,0x80

  在 L1 行,執(zhí)行結(jié)果顯示編碼為一個(gè) 0x04bb,其實(shí)這是一個(gè)轉(zhuǎn)換錯(cuò)誤,如果使用其他漢字,如“哈”,編譯都將無(wú)法通過(guò)。也就是說(shuō) Linux 環(huán)境下,直接聲明中文寬字符串是不正確的,編譯器不能夠正確轉(zhuǎn)換。

  而在中文 windows 下使用相同測(cè)試代碼,則會(huì)在 L1 處出現(xiàn)區(qū)別,ws 中的 wchar_t 元素十六進(jìn)制值是 0x4e00,這是漢字“一”的 Unicode 編碼。

  處理編碼問(wèn)題的經(jīng)驗(yàn)總結(jié)

  首先,這里先簡(jiǎn)單說(shuō)明一下 Unicode 和 UTF8 的關(guān)系:Unicode 的實(shí)現(xiàn)方式和它的編碼方式并不相同,UTF8 就是其實(shí)現(xiàn)之一。比方使用 UltraEdit 打開 UTF8 編碼的中文文件,使用 16 進(jìn)制查看,可以發(fā)現(xiàn)看到的中文對(duì)應(yīng)部分應(yīng)當(dāng)是 Unicode 編碼,每個(gè)中文字長(zhǎng)度 2 字節(jié)—— UltraEdit 在這里已經(jīng)做了轉(zhuǎn)化;如果直接查看其二進(jìn)制文件,可以發(fā)現(xiàn)是 3 字節(jié)。但兩者的差別僅在于 Unicode 向 UTF8 做了數(shù)學(xué)上的轉(zhuǎn)化。

  其次,關(guān)于第三方庫(kù)的選擇,應(yīng)當(dāng)綜合考慮項(xiàng)目的需求。一般的文本字符轉(zhuǎn)換,系統(tǒng)的庫(kù)函數(shù)已經(jīng)可以滿足需求,實(shí)現(xiàn)也很簡(jiǎn)單;如果需要針對(duì)不同地區(qū)的語(yǔ)言、文字、習(xí)慣進(jìn)行編程,需要更為豐富的功能,當(dāng)然選擇成熟的第三方工具可以事半功倍。

  ,從邏輯上保持字符串的編碼正確,需要注意幾條一般規(guī)律:

  編碼選擇:多國(guó)語(yǔ)言環(huán)境的編程,以使用 UTF 編碼為原則,減少字符集轉(zhuǎn)換。

  string 并不包含編碼信息,但是編碼確定了 string 的二進(jìn)制內(nèi)容。

  讀寫一致:讀入時(shí)使用的字符集要與寫出時(shí)使用的一致。如果不需要改變字符串內(nèi)容,僅僅是將字符串讀入、再寫出,建議不要調(diào)整任何字符集——即使程序使用的系統(tǒng)默認(rèn)字符集 A 與文件的實(shí)際編碼 B 不符合,寫出的字符串依然會(huì)是正確的 B 編碼。

  讀入已知:對(duì)于必須處理、解析或顯示的字符串,從文件讀入時(shí)必須知道它的編碼,避免處理字符串的代碼簡(jiǎn)單使用系統(tǒng)默認(rèn)字符集;即便對(duì)于程序從系統(tǒng)中收集到的內(nèi)存字符串,也應(yīng)知道其符合的編碼格式——一般為系統(tǒng)默認(rèn)字符集。

  避免直接使用 Unicode:這里是說(shuō)將非 ASCII 編碼的 16 進(jìn)制或者 10 進(jìn)制數(shù)值用 &# 與 ; 包含起來(lái)的使用方式,例如將中文“一”寫成“&#4e00;”。這種方法的實(shí)質(zhì)是 Unicode 編碼直接寫入文件。這不僅會(huì)降低代碼的通用性、輸出文件的可讀性,處理起來(lái)也很困難。比如法文字符在其他字符集中是大于 80H 的單字節(jié)字符,程序同時(shí)要支持中文的時(shí)候,很有可能會(huì)將多字節(jié)的中文字符錯(cuò)誤割裂。

  避免陷入直接的字符集編程:國(guó)際化、本地化的工具已經(jīng)比較成熟,非純粹做編碼轉(zhuǎn)換的程序員沒有必要自己去處理不同編碼表的映射轉(zhuǎn)換問(wèn)題。

  Unicode/UTF8 并不能解決一切亂碼問(wèn)題:Unicode 可以說(shuō)是將世界語(yǔ)言統(tǒng)一起來(lái)的一套編碼。但是這并不意味著在一個(gè)系統(tǒng)中可以正常顯示的按照 UTF8 編碼的文件,在另一個(gè)系統(tǒng)中也可以正常顯示。例如,在中文的 UTF8 編碼或者 Unicode 編碼在沒有東亞語(yǔ)言包支持的法文系統(tǒng)中,依然是不可識(shí)別的亂碼——盡管 UTF8、Unicode 它們都支持。


  
關(guān)鍵詞:C/C++ 編程中多國(guó)語(yǔ)言處理C/C++

版權(quán)與免責(zé)聲明

凡本網(wǎng)注明“出處:維庫(kù)電子市場(chǎng)網(wǎng)”的所有作品,版權(quán)均屬于維庫(kù)電子市場(chǎng)網(wǎng),轉(zhuǎn)載請(qǐng)必須注明維庫(kù)電子市場(chǎng)網(wǎng),http://m.58mhw.cn,違反者本網(wǎng)將追究相關(guān)法律責(zé)任。

本網(wǎng)轉(zhuǎn)載并注明自其它出處的作品,目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點(diǎn)或證實(shí)其內(nèi)容的真實(shí)性,不承擔(dān)此類作品侵權(quán)行為的直接責(zé)任及連帶責(zé)任。其他媒體、網(wǎng)站或個(gè)人從本網(wǎng)轉(zhuǎn)載時(shí),必須保留本網(wǎng)注明的作品出處,并自負(fù)版權(quán)等法律責(zé)任。

如涉及作品內(nèi)容、版權(quán)等問(wèn)題,請(qǐng)?jiān)谧髌钒l(fā)表之日起一周內(nèi)與本網(wǎng)聯(lián)系,否則視為放棄相關(guān)權(quán)利。

廣告
OEM清單文件: OEM清單文件
*公司名:
*聯(lián)系人:
*手機(jī)號(hào)碼:
QQ:
有效期:

掃碼下載APP,
一鍵連接廣大的電子世界。

在線人工客服

買家服務(wù):
賣家服務(wù):
技術(shù)客服:

0571-85317607

網(wǎng)站技術(shù)支持

13606545031

客服在線時(shí)間周一至周五
9:00-17:30

關(guān)注官方微信號(hào),
第一時(shí)間獲取資訊。

建議反饋

聯(lián)系人:

聯(lián)系方式:

按住滑塊,拖拽到最右邊
>>
感謝您向阿庫(kù)提出的寶貴意見,您的參與是維庫(kù)提升服務(wù)的動(dòng)力!意見一經(jīng)采納,將有感恩紅包奉上哦!