青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得

我在解決亂碼上面實(shí)際走了不少?gòu)澛罚隽撕芏鄬?shí)驗(yàn),查了很多資料。在這里做下筆記,希望后來(lái)者可以明白,少走些彎路。

從最熟悉的兩種字符編碼說(shuō)起

除了一些舊的、沒(méi)有考慮到兼容性的網(wǎng)頁(yè)還在用gbk做編碼外,大部分的網(wǎng)頁(yè)都已經(jīng)用utf-8做編碼了。但是最令人頭疼的是,windows的控制臺(tái)是很不好顯示utf-8的。有明君為我大C++寫(xiě)了兩個(gè)函數(shù),是正確的、好用的(除了用std::string做返回值讓我等效率黨有點(diǎn)覺(jué)得不爽之外……還是挺方便的).

#include <string>
#include 
<windows.h>
using std::string;

//gbk 轉(zhuǎn) utf8
string GBKToUTF8(const string& strGBK)
{
    
string strOutUTF8 = "";
    WCHAR 
* str1;
    
int n = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0);
    str1 
= new WCHAR[n];
    MultiByteToWideChar(CP_ACP, 
0, strGBK.c_str(), -1, str1, n);
    n 
= WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);
    
char * str2 = new char[n];
    WideCharToMultiByte(CP_UTF8, 
0, str1, -1, str2, n, NULL, NULL);
    strOutUTF8 
= str2;
    delete[]str1;
    str1 
= NULL;
    delete[]str2;
    str2 
= NULL;
    
return strOutUTF8;
}

//utf-8 轉(zhuǎn) gbk
string UTF8ToGBK(const string& strUTF8)
{
    
int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);
    unsigned 
short * wszGBK = new unsigned short[len + 1];
    memset(wszGBK, 
0, len * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 
0, (LPCTSTR)strUTF8.c_str(), -1, wszGBK, len);

    len 
= WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
    
char *szGBK = new char[len + 1];
    memset(szGBK, 
0, len + 1);
    WideCharToMultiByte(CP_ACP,
0, wszGBK, -1, szGBK, len, NULL, NULL);
    
//strUTF8 = szGBK;
    std::string strTemp(szGBK);
    delete[]szGBK;
    delete[]wszGBK;
    
return strTemp;
}

這玩意兒不跨平臺(tái),因?yàn)樗玫搅藈indows api。我之所以把它放到跨平臺(tái)編程上面來(lái),是因?yàn)樽址幋a這東西只有到跨平臺(tái)的時(shí)候才顯得坑爹。


接著我是不是要介紹那倆函數(shù)一下?

int MultiByteToWideChar(
  _In_       UINT CodePage, 
/*代碼頁(yè)是Windows下字符編碼的叫法,gbk是936,utf-8是65001,CP_ACP是ANSI*/
  _In_       DWORD dwFlags, 
/*選項(xiàng)標(biāo)志,轉(zhuǎn)換類(lèi)型,設(shè)0就行了*/
  _In_       LPCSTR lpMultiByteStr, 
/*多字節(jié)字符串*/
  _In_       
int cbMultiByte, /*字符串要處理的長(zhǎng)度,如果是-1函數(shù)就會(huì)處理整個(gè)字符串*/
  _Out_opt_  LPWSTR lpWideCharStr, 
/*輸出的寬字符串緩存,如果為空就返回需要的寬字符串長(zhǎng)度*/
  _In_       
int cchWideChar /*寬字符串緩存的長(zhǎng)度,當(dāng)然如果寬字符串為空,這個(gè)設(shè)0就可以了*/
);

int WideCharToMultiByte(
  _In_       UINT CodePage,
  _In_       DWORD dwFlags,
  _In_       LPCWSTR lpWideCharStr,
  _In_       
int cchWideChar,
  _Out_opt_  LPSTR lpMultiByteStr,
  _In_       
int cbMultiByte, /*前面的基本與MultiByteToWideChar都相同,就不解釋了*/
  _In_opt_   LPCSTR lpDefaultChar, 
/*填0即可*/
  _Out_opt_  LPBOOL lpUsedDefaultChar 
/*填0即可*/
);

這兩個(gè)函數(shù)分別是將多字節(jié)字符串轉(zhuǎn)換為寬字符字符串 和 將寬字符字符串轉(zhuǎn)換為多字節(jié)字符串(在此處暈倒的童鞋們我沒(méi)有對(duì)不起你們……是M$那家伙對(duì)不起你們)。我早就說(shuō)過(guò)Windows API 的界面不友好,這么多不知道干嘛嗎用的參數(shù),全部填0就對(duì)了。要是iconv(),它貌似只有4個(gè)參數(shù),這才是好的榜樣。


寬字符?多字節(jié)?

這是Windows給它們起的名字,讓人摸不著頭腦。

  • 寬字符:就是Unicode。它雷打不動(dòng)地用2個(gè)字節(jié)(0x0000 - 0xFFFF),表示所有我們平常能見(jiàn)到的字符,具體的表格見(jiàn):http://unicode-table.com

  • 多字節(jié):就是除了Unicode外其他的。我們熟悉的gbk, utf-8, big5,統(tǒng)統(tǒng)歸入多字節(jié)。

寬字符之所以叫做寬字符,是因?yàn)樗且粋€(gè)寬一點(diǎn)的字符。那什么是短字符……就是ascii了,1個(gè)字節(jié)1個(gè)字符絕對(duì)夠短,而且只能表示256個(gè)西歐字符。寬字符呢,是2個(gè)字節(jié)1個(gè)字符。寬一點(diǎn),但還是可以識(shí)別到一個(gè)字符是哪里的。而多字節(jié)呢,就是它在計(jì)算機(jī)里表示成多個(gè)字節(jié),但是沒(méi)有辦法識(shí)別那里到那里是一個(gè)字符。

我不喜歡這兩個(gè)函數(shù)的命名。如果按照Python的命名,MultiByteToWideChar 應(yīng)該叫 decode(解碼),WideCharToMultiByte 應(yīng)該叫 encode(編碼)。


所以呢?

如你所見(jiàn),多字節(jié)無(wú)法準(zhǔn)確識(shí)別字符的長(zhǎng)度,處理起來(lái)就會(huì)很麻煩。而寬字符大多時(shí)候雖然比多字節(jié)多耗費(fèi)一點(diǎn)空間,但是處理起來(lái)方便。比如正則表達(dá)式處理,引擎是基于字符去匹配的,寬字符可以兩個(gè)字節(jié)兩個(gè)字節(jié)跳著匹配,而多字節(jié)就會(huì)匹配錯(cuò)誤。

比如有一個(gè)詞“程序”=0xB3CCD0F2(gbk),我想匹配“”=0xCCD0(gbk),正則庫(kù)會(huì)替我把中間那兩個(gè)字節(jié)匹配了。用在C里用wchar_t,C++里用std::wstring,我們可以很準(zhǔn)確的,無(wú)錯(cuò)誤地匹配到我們想要的子串,因?yàn)橐嬖诘臅r(shí)候是逐字(而不是逐字節(jié))進(jìn)行比較的。

1 >>> str1 = ""
2 >>> str2 = "程序"
3 >>> print re.findall(str1, str2)
4 ['\xcc\xd0']
5 >>> print re.findall(str1.decode("gbk"), str2.decode("gbk"))
6 []

所以在處理字符串的時(shí)候,但凡要處理中文,要先把用戶給的字符串解碼成Unicode。處理完之后顯示出來(lái)或者保存,再編碼成需要的charset。


Appendix

在不同的地方用不同的編碼:

  • 網(wǎng)絡(luò)文本(如網(wǎng)頁(yè))傳輸一般用utf-8,因?yàn)橛猩倭恐形模蟛糠质怯⑽摹?/em>
  • 在保存為本地文件的時(shí)候,應(yīng)該保存為Unicode,因?yàn)楸镜卮鎯?chǔ)資源豐富,且可以節(jié)省時(shí)間,實(shí)時(shí)解碼畢竟也是O(N^2)啊。
  • 顯示出來(lái)應(yīng)該用系統(tǒng)的編碼,中文Windows為gbk,繁體Windows為Big5,Linux一律為UTF-8。
  • 源代碼里的少量中文串盡量用"\x????\x????"來(lái)表示,如果有大量中文建議用gettext或者資源之類(lèi)的以外掛的方式讀入。
  • Qt內(nèi)部使用Unicode,所以編寫(xiě)Qt應(yīng)用時(shí)顯示文字直接傳遞寬字符串即可。
  • NTFS的文件名、路徑都是用GBKUTF16LE編碼的,所以如果Windows下用戶輸入的是路徑就無(wú)需解碼了。


posted on 2013-10-28 22:49 Shihira 閱讀(7283) 評(píng)論(8)  編輯 收藏 引用 所屬分類(lèi): 跨平臺(tái)編程

評(píng)論

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得[未登錄](méi) 2013-10-29 12:45 jacky

string作為返回值也沒(méi)什么大問(wèn)題,現(xiàn)代編譯器(返回值優(yōu)化,右值引用)都能優(yōu)化掉多余的拷貝。  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2013-10-29 13:52 Shihira

@jacky
謝謝提醒,即將更正。  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得[未登錄](méi) 2013-10-30 10:07 春秋十二月

那兩個(gè)函數(shù)和我代碼庫(kù)中的差不多哈  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2013-11-08 10:38 海邊泡沫

樓主還是沒(méi)有理解寬字符和多字符的本質(zhì)。
它們的本質(zhì)絕對(duì)不是為了儲(chǔ)存,而是為了溝通。
從儲(chǔ)存的角度講,我們可以用一個(gè)字符表示一個(gè)字母,也可以用兩個(gè)字節(jié)表示一個(gè)字母和漢字,也可以用三個(gè)、四個(gè)字符。所以不管是UNICODE也好,還是GBK、BIG5也好,它們沒(méi)有本質(zhì)的區(qū)別,它們的本質(zhì)都是固定寬度的。而從溝通的角度講,只要知道這些固定寬度的字節(jié)串的采取的是什么編碼,就可以相互轉(zhuǎn)換。
從儲(chǔ)存的角度講,既然都是固定寬度的,我們其實(shí)不僅可以用wchar[]來(lái)儲(chǔ)存UNICODE,也可以用wchar[]來(lái)儲(chǔ)存BIG5或GBK。而事實(shí)上,除了UNICODE,Windows中其它的所有編碼儲(chǔ)存都是用char[],包括UTF-8。這也是為什么WINDOWS中的轉(zhuǎn)換函數(shù)叫MultiByteToWideChar了,這一組函數(shù)意義很明確,不存在設(shè)計(jì)問(wèn)題。
那么還有用char[]的必要嗎?難道這只是歷史遺留問(wèn)題嗎?當(dāng)然不是了。這也是我要說(shuō)的溝通的本質(zhì)了。
從溝通的本質(zhì)講,我們?cè)谟?jì)算機(jī)中和網(wǎng)絡(luò)上所傳輸?shù)摹⑺容^的,一切的本質(zhì)都是字節(jié)流,嚴(yán)格來(lái)說(shuō),是只用到了最低7位,低8位為0的字節(jié)流,也就是unsigned char[]。所以網(wǎng)絡(luò)上廣范使用UTF-8不是因?yàn)樗】臻g,而是因?yàn)樗亲止?jié)流,是最適合傳輸?shù)谋磉_(dá)字符的編碼方案。
UTF-8是寬度不固定的。我認(rèn)為微軟的專(zhuān)家早就認(rèn)識(shí)到了這個(gè)本質(zhì),所以不管一種編碼的寬度是否固定,他們都把他們叫MiltiByte,而只有UNICODE叫WideChar。
最后再說(shuō)一點(diǎn),從溝通的角度講,不僅字符是字節(jié)流,數(shù)字也是字節(jié)流,而且在傳輸數(shù)字的時(shí)候還要考慮字節(jié)序。這也是hton和ntoh這樣一組函數(shù)存在的原因。  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2013-11-09 16:41 紅色代碼

windows下的字符轉(zhuǎn)換確實(shí)夠坑爹的  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2013-11-26 01:12 放屁阿狗

iconv why not ?  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2014-03-04 17:07 huxi

"NTFS的文件名、路徑都是用GBK編碼的,所以如果Windows下用戶輸入的是路徑就無(wú)需解碼了。"
貌似NTFS的路徑是UTF16LE編碼,
http://zh.wikipedia.org/wiki/NTFS

文件系統(tǒng)支持最長(zhǎng) 32767 個(gè) Unicode 字符的的路徑[57]。但每個(gè)路徑組成部分(目錄或文件名)最多只允許包含 255 個(gè)字符[57],同時(shí)某些特定的名稱需要用于保存 NTFS 將元數(shù)據(jù),因此禁止用作普通的文件/目錄名。NTFS 的元數(shù)據(jù)都存放在卷的根目錄下,因此上文所述的文件名要求也僅限于根目錄。被 NTFS 保留的名稱有:$MFT、$MFTMirr、$LogFile、$Volume、$AttrDef、.(點(diǎn))、$Bitmap、$Boot、$BadClus、$Secure、$Upcase,以及 $Extend[2]。在這些項(xiàng)目中,.(點(diǎn))和 $Extend 是目錄類(lèi)型,其它項(xiàng)目均為文件類(lèi)型。  回復(fù)  更多評(píng)論   

# re: Windows API 字符編碼轉(zhuǎn)換以及一些解釋和心得 2014-03-27 13:50 Shihira

@huxi
It makes sense. 謝謝提醒,已更正.  回復(fù)  更多評(píng)論   


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


導(dǎo)航

統(tǒng)計(jì)

  • 隨筆 - 12
  • 文章 - 0
  • 評(píng)論 - 33
  • 引用 - 0

公告

  • 大家有什么不清楚的,盡管提問(wèn),或者發(fā)郵件給我
  • 由于本人的水平有限,經(jīng)驗(yàn)不足,難免會(huì)出現(xiàn)錯(cuò)誤;請(qǐng)各位不吝賜教,以便在今后的工作和學(xué)習(xí)中改正;
  • 2012/8/1晚8:00更正了(七)和(八)中的代碼;

留言簿(2)

隨筆分類(lèi)

搜索

  •  

最新隨筆

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              夜夜精品视频一区二区| 国产精品稀缺呦系列在线| 午夜精品久久久久久99热| 狂野欧美一区| 久久久久一区二区三区四区| 欧美视频在线观看| 亚洲日本中文字幕| 亚洲国产婷婷综合在线精品| 欧美在线观看视频| 欧美一区二区黄| 国产精品高潮久久| 一区二区三区三区在线| 一本久久综合亚洲鲁鲁| 欧美高清视频在线观看| 欧美成人精品三级在线观看 | 欧美日韩国产bt| 欧美成人国产| 亚洲第一在线视频| 久久一二三国产| 欧美96在线丨欧| 在线日韩中文| 女人天堂亚洲aⅴ在线观看| 免费av成人在线| 在线免费观看一区二区三区| 久久福利毛片| 久久偷看各类wc女厕嘘嘘偷窃| 国产亚洲精品资源在线26u| 亚洲欧美在线免费| 欧美一区二区三区久久精品| 国产精品素人视频| 香蕉久久夜色| 久久香蕉国产线看观看av| 黑人中文字幕一区二区三区| 久久激情久久| 女女同性精品视频| 亚洲欧洲偷拍精品| 欧美激情性爽国产精品17p| 欧美~级网站不卡| 亚洲欧洲免费视频| 欧美日韩午夜剧场| 亚洲欧美激情视频| 久久男人av资源网站| 亚洲国产成人av在线| 欧美激情综合亚洲一二区 | 欧美激情二区三区| 99re热精品| 国产精品日本精品| 久久精品女人| 亚洲精品社区| 欧美一区二区三区啪啪| 影音国产精品| 欧美日韩国产一级| 欧美在线黄色| 亚洲国产精品电影| 亚洲男人的天堂在线aⅴ视频| 国产精品综合视频| 欧美成人r级一区二区三区| 日韩午夜电影| 久久中文在线| 亚洲午夜在线观看视频在线| 国模私拍一区二区三区| 欧美r片在线| 亚洲欧美国产精品va在线观看| 欧美成人在线免费观看| 亚洲综合色自拍一区| 狠狠色综合色区| 欧美日韩在线一区二区三区| 欧美在现视频| 在线视频你懂得一区| 久久综合九色欧美综合狠狠| 亚洲无人区一区| 亚洲国产精品一区二区三区 | 欧美日韩国产在线一区| 香蕉久久夜色精品国产| 亚洲国产精品综合| 久久精品观看| 一区二区欧美日韩| 伊人精品成人久久综合软件| 国产精品久久久久久妇女6080| 久久久99免费视频| 在线一区二区三区做爰视频网站| 免费不卡亚洲欧美| 亚洲欧美日韩人成在线播放| 91久久久国产精品| 狠狠久久亚洲欧美| 欧美三级在线| 欧美a级片网站| 久久久久久久999| 亚洲欧美日韩成人高清在线一区| 亚洲精品国产视频| 欧美激情精品久久久久久大尺度| 久久蜜桃精品| 久久久99久久精品女同性| 亚洲一区在线免费观看| 99re这里只有精品6| 亚洲国产欧美在线| 在线成人激情视频| 激情小说另类小说亚洲欧美| 国产日韩精品视频一区二区三区| 欧美特黄一区| 国产精品video| 国产精品久久久久久久久久免费看| 欧美激情一区二区三区高清视频| 久久久久国产精品人| 欧美中文在线观看| 欧美在线亚洲综合一区| 欧美在线国产| 久久精品欧美日韩| 久久五月天婷婷| 另类综合日韩欧美亚洲| 免费观看一区| 欧美福利电影在线观看| 欧美激情综合色| 欧美日韩一区二区国产| 欧美手机在线| 国产欧美日韩一级| 国模一区二区三区| 亚洲第一二三四五区| 91久久亚洲| 制服丝袜亚洲播放| 午夜视频在线观看一区二区| 性欧美精品高清| 久久先锋资源| 亚洲国产精品一区制服丝袜| 亚洲精品欧洲| 亚洲一区二区三区免费视频| 欧美一区二区在线播放| 久久综合网络一区二区| 欧美精品国产一区二区| 国产精品v欧美精品v日韩| 国产日韩欧美在线看| 激情欧美丁香| 亚洲伦理网站| 午夜国产精品视频免费体验区| 久久久青草青青国产亚洲免观| 欧美成人午夜免费视在线看片| 亚洲国产欧美国产综合一区| 一区二区三区精品国产| 久久精品国产免费| 欧美另类在线观看| 国产一区二区黄色| 亚洲日本欧美| 欧美一区二区三区免费观看视频 | 欧美二区在线播放| 国产精品亚洲аv天堂网| 曰韩精品一区二区| 亚洲一区欧美一区| 麻豆久久婷婷| 在线亚洲精品福利网址导航| 久久精品成人一区二区三区蜜臀 | 欧美成人中文| 国产欧美短视频| 日韩网站在线看片你懂的| 欧美一级大片在线观看| 欧美电影免费观看高清| 亚洲永久免费| 欧美日韩爆操| 在线观看不卡| 亚洲女女女同性video| 免费国产一区二区| 一区二区三区欧美视频| 久久综合色播五月| 国产欧美日韩视频一区二区| 亚洲美女免费精品视频在线观看| 久久狠狠亚洲综合| 99精品视频免费全部在线| 另类亚洲自拍| 国产亚洲精品久久飘花| 亚洲夜间福利| 亚洲人体一区| 免费观看一级特黄欧美大片| 国产欧美一区二区精品仙草咪| 一级日韩一区在线观看| 美女精品一区| 久久成人免费电影| 国产精品欧美一区二区三区奶水| 亚洲精品九九| 欧美国产综合| 免费观看成人网| 亚洲观看高清完整版在线观看| 欧美一区二区三区四区高清| 宅男精品视频| 欧美日韩亚洲精品内裤| 一本色道久久综合亚洲精品不| 蜜臀a∨国产成人精品| 欧美一区二区免费视频| 国产精品日本| 欧美一区二区三区四区视频| 亚洲最新中文字幕| 欧美激情亚洲自拍| 亚洲精品无人区| 亚洲激情在线观看| 欧美激情91| 日韩视频在线你懂得| 亚洲高清电影| 欧美激情导航| 中文日韩在线| 亚洲一级影院| 国产三级精品三级| 久久综合九色综合欧美就去吻|