• <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>

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

            我在解決亂碼上面實(shí)際走了不少彎路,做了很多實(shí)驗(yàn),查了很多資料。在這里做下筆記,希望后來者可以明白,少走些彎路。

            從最熟悉的兩種字符編碼說起

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

            #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;
            }

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


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

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

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


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

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

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

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

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

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


            所以呢?

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

            比如有一個詞“程序”=0xB3CCD0F2(gbk),我想匹配“”=0xCCD0(gbk),正則庫會替我把中間那兩個字節(jié)匹配了。用在C里用wchar_t,C++里用std::wstring,我們可以很準(zhǔn)確的,無錯誤地匹配到我們想要的子串,因?yàn)橐嬖诘臅r候是逐字(而不是逐字節(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 []

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


            Appendix

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

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


            posted on 2013-10-28 22:49 Shihira 閱讀(7189) 評論(8)  編輯 收藏 引用 所屬分類: 跨平臺編程

            評論

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            文件系統(tǒng)支持最長 32767 個 Unicode 字符的的路徑[57]。但每個路徑組成部分(目錄或文件名)最多只允許包含 255 個字符[57],同時某些特定的名稱需要用于保存 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 是目錄類型,其它項(xiàng)目均為文件類型。  回復(fù)  更多評論   

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

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

            導(dǎo)航

            統(tǒng)計(jì)

            • 隨筆 - 12
            • 文章 - 0
            • 評論 - 33
            • 引用 - 0

            公告

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

            留言簿(2)

            隨筆分類

            搜索

            •  

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            久久精品午夜一区二区福利| 久久精品国产亚洲AV不卡| www.久久热.com| 成人亚洲欧美久久久久| 香港aa三级久久三级老师2021国产三级精品三级在 | 大美女久久久久久j久久| 国产成人无码精品久久久久免费| 久久国产香蕉视频| 亚洲AV无码一区东京热久久| 日本道色综合久久影院| 九九精品久久久久久噜噜| 国产精品久久国产精品99盘 | 亚洲国产精品综合久久一线| 久久久女人与动物群交毛片| 久久综合一区二区无码| 国产99精品久久| 亚洲成色www久久网站夜月 | 久久99精品久久久久久hb无码 | 91久久精品视频| 久久久久亚洲AV片无码下载蜜桃 | 久久99国产精品久久99果冻传媒| 欧美亚洲日本久久精品| 91久久精品国产成人久久| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 国产亚洲精品美女久久久| 亚洲国产精品无码久久九九| 国产日韩久久免费影院| 久久精品国产福利国产秒| 亚洲国产精品成人久久| 亚洲中文字幕无码久久2017| 2020久久精品亚洲热综合一本| 久久久久综合国产欧美一区二区| 青青青国产成人久久111网站| 久久精品欧美日韩精品| 久久久久久九九99精品| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 国产精品伦理久久久久久| 久久国产精品99久久久久久老狼| 国产亚洲精品自在久久| 国产精品一久久香蕉产线看| 国产一区二区精品久久|