c++中的字符串編碼處理方法
今天由于在項目中用到一些與c++混合開發(fā)的東西 ,需要通過socket與c++那邊交換數(shù)據(jù),沒啥特別的,字節(jié)碼而已,兩邊確定一種編碼規(guī)則就行了。我們確定的utf-8。關(guān)于c++的 這種又是寬字節(jié) 又是messageboxw 又是messageboxa 的 ,說實話相比c#而言 搞的確實非常的和稀泥 搞的非常的糊,別說新手 有些不是新手的都搞不明白。
字符串字面量怎么被編碼成字節(jié)的
什么是字符串?c#里的 string?c++里的char* ? 字符串的本質(zhì)是什么?字符串不過是一個特殊的數(shù)據(jù)字節(jié)包裝 帶有編碼信息,特別是c++的 更原始 更便于我們想清楚這個底層,其實其他的已經(jīng)迎刃而解了。首先我們無論如何確定一個東西 那就是交換的東西是字節(jié)碼 ,說白了 也就是c++ 里的char [ ] 也就是char *,在我不管你編碼的情況下 我新建vc++項目 在代碼里這樣寫:
char str1[] = "中a"; printf("%s\r\n", str1);
能不能輸出東西?能不能輸出中文 當(dāng)然能,那這個str1 字節(jié)碼到底是什么字節(jié)碼, 只要我們把這個搞明白就可以了。一切未知的恐懼源于不明白。我們先調(diào)試c++代碼 取到字節(jié)碼,然后編寫下面這兩句c#代碼:
byte[] bts2 = new byte[] { 0xd6, 0xd0, 0x61 }; console.writeline(encoding.getencoding("gb2312").getstring(bts2));
正常輸出了c++代碼里的中文 由此可見c++里默認(rèn)代碼到字節(jié) 的字面量轉(zhuǎn)換 就是gb2312 ,就這樣而已。就這樣而已 ,真的就這么點(diǎn)東西 ,不要探究是什么機(jī)制驅(qū)使vc++默認(rèn)把字符串轉(zhuǎn)換到了gb2312編碼,事情不要歪呀歪的想想復(fù)雜了,人的精力是有限的 要放在有作用的地方。你看c++里是char [ ] 還不像c#的string經(jīng)過包裝的 更便于你想明白這個過程。不是說c++有std庫么 不是有string 么 還沒講呢 ,c++這門語言呢又好又不好 設(shè)計特點(diǎn)是暴露的細(xì)節(jié)多 各個細(xì)節(jié)你都可以自己控制 讓會用的人知道自己在做什么 ,但是也有些坑,其實string 就是char[] 的變種而已。你看c++里 在你琢磨不透的情況下悄然在你不知情編碼的情況下轉(zhuǎn)換成了字節(jié)碼,c#的string 封裝的 不會給你這個機(jī)會 有明確的encoding庫調(diào)用指定編碼。
窄字符和寬字符 ,怎么個寬法
c++里字符串的字面量分為兩種 一種是普通的窄字符 ,也就是普通的char [ ] 一個元素占1字節(jié), 另一種是寬字符 wchar_t [ ] 一個元素占2字節(jié),_t("中a") 或者l"中a" 這種就是強(qiáng)行表示unicode寬字符字面量。 寬字符 怎么個寬法呢,我們說他是unicode 也就是utf-16,我們用c#進(jìn)行驗證:
byte[] bts3 = new byte[] { 0x2d,0x4e, 0x61,0x00, }; console.writeline(encoding.unicode.getstring(bts3));
好了,這就明朗了,c++這玩意兒 由于歷史遺留原因,直接在代碼書寫字符串字面量搞了兩套標(biāo)準(zhǔn) 窄字符和寬字符 ,你看上面的同字符里面的字節(jié)碼整的兩套標(biāo)準(zhǔn) 這就很扯,整的被迫大多數(shù)c++的函數(shù) 或者接口都要按照這個套路玩。就有了看到的messageboxa ()接受char[]窄字符參數(shù),messageboxw()接受寬字符參數(shù) ,不要有誤區(qū)哈 覺得char[ ] 就不能輸出中文 ,能不能是由對應(yīng)的地方能不能解析這個字節(jié)碼決定的 而不是其他。
關(guān)于utf-8
utf-8的現(xiàn)實意義更大于編程的字面量意義 ,為什么這么說,現(xiàn)在網(wǎng)絡(luò) 數(shù)據(jù)交換都是utf-8 編碼,c++編程 字面量 沒有所謂utf-8這個說法 ,utf-8是一種落地編碼,落地編碼 懂嗎?就像圖像編程 保存最終格式有.jpg .png,utf-8 他是變長的 對于字符串處理會出現(xiàn)很多問題 不利于程序處理,圖像編程中不管你jpg png格式也好載入到內(nèi)存中最后都是易于處理的bmp內(nèi)存映像。編程中都是unicode因為2字節(jié)代表一個字符 標(biāo)標(biāo)準(zhǔn)準(zhǔn)的 是對齊的,利于編程處理。還有 utf-8 一個中文3字節(jié) 其實比utf-16 一個中文2字節(jié) 多, 但是如果是英文的話 就是1字節(jié) 可以實現(xiàn)unicode到ascii的無縫轉(zhuǎn)換 可以處理一些老舊系統(tǒng)的兼容問題。 c++里unicode可以通過手段轉(zhuǎn)換為utf-8:
void unicodetoutf8(const wchar_t* unicode,char utf82[],int * lenout) { int len; len = widechartomultibyte(cp_utf8, 0, unicode, -1, null, 0, null, null); char szutf82[50] = { 0 }; *lenout = len; widechartomultibyte(cp_utf8, 0, unicode, -1, utf82, len, null, null); }
關(guān)于vc++項目屬性里的設(shè)置字符集
什么意思呢:
當(dāng)選擇“使用unicode字符集”時,編譯器會增加宏定義——unicode;而選擇“使用多字節(jié)字符集”時,編譯器則不會增加宏定義——unicode。https://blog.csdn.net/huashuolin001/article/details/95620424
當(dāng)選用“使用unicode字符集”時,調(diào)用函數(shù)messagebox,實際使用的是messageboxw,messageboxw關(guān)于字符串的入?yún)㈩愋褪莑pcwstr,使用messagebox時,字符串前需加l::messagebox(null, l"這是一個測試程序!", l"title", mb_ok);
多字節(jié),默認(rèn)的窄字符char[]帶中文 就是典型的多字節(jié),接上面章節(jié)說明 多字節(jié)+中文 對于字符串處理分割 會帶來很多問題,所以帶中文請盡量使用寬字符。然后另一個 基于gb2312和unicode編碼我就不細(xì)說了哈,如果你想你的程序能夠賣到國外在世界范圍內(nèi)使用,那么請使用unicode,也就是 l" " 寬字符。c++里這些概念搞的比較糊 ,我描述的這些也是個意會 ,也許某些細(xì)節(jié)部分說錯了 像原來文章里那些評論里那樣 尖銳的指出來 不怕批評。
最后 ,一些測試的大雜燴代碼:
// consoleapplication1.cpp : 定義控制臺應(yīng)用程序的入口點(diǎn)。 // #include "stdafx.h" #include <iostream> #include "h1.h" #include "fqtabdata.h" #include "test1.h" #include <windows.h> #include <string> #include <iomanip> #include <type_traits> using namespace std; //引用的使用方式 void test1(int &r){ r = r+1; } void unicodetoutf8(const wchar_t* unicode,char utf82[],int * lenout) { int len; len = widechartomultibyte(cp_utf8, 0, unicode, -1, null, 0, null, null); char szutf82[50] = { 0 }; *lenout = len; widechartomultibyte(cp_utf8, 0, unicode, -1, utf82, len, null, null); } int _tmain(int argc, _tchar* argv[]) { setlocale(lc_all, "");//注意控制臺輸出要先加上這句哈要不然無法輸出中文 wchar_t wstr2[] = l"中a"; wprintf(l"%ls\r\n", wstr2); char str1[] = "中ab"; printf("%s\r\n", str1); return 0; //關(guān)于c++里的編碼問題 // 并非 不在在項目屬性里設(shè)置編碼字符集 為unicode 就不能顯示中文 //char str11[] = "中a"; printf("%s", str11); //這段代碼照樣顯示中文,中a被編譯器編成3個元素存在str11 里+\0結(jié)尾 //當(dāng)選擇“使用unicode字符集”時,編譯器會增加宏定義——unicode;而選擇“使用多字節(jié)字符集”時,編譯器則不會增加宏定義——unicode。 //https://blog.csdn.net/huashuolin001/article/details/95620424 //當(dāng)選用“使用unicode字符集”時,調(diào)用函數(shù)messagebox,實際使用的是messageboxw,messageboxw關(guān)于字符串的入?yún)㈩愋褪莑pcwstr, //使用messagebox時,字符串前需加l //::messagebox(null, l"這是一個測試程序!", l"title", mb_ok); //關(guān)于這個l ,等同于_t("") tchar 這些玩意兒他們都有同等意義 //可以傻瓜的理解 l 本身就是搞一個寬字符型 字符串 ,每個字符占2字節(jié) //wchar_t ws[] = l"國家"; //設(shè)置為unicode 就意味著寬字符 就意味著字符串 要加l //就像前面的 好多函數(shù)接口有兩種版本 messageboxa messageboxw , //messageboxw就意味著你要傳一個寬字符數(shù)組進(jìn)去 也就是 wchar_t 或者l"dd" //注意多字節(jié)字符集是一個很容易讓人費(fèi)解的玩意兒, //我們說 utf-8是 一種unicode的落地編碼 //編程里都是用 unicode 不管項目設(shè)沒設(shè)置unicode字符集 wchar_t ws[] = l"國家"; 得到的都是寬字符串 //但是編程代碼里 沒有utf-8 這一說法 utf-8是變長的 也就是多字節(jié) 他是一種編碼落地 //你想想你整個變長 別人接口怎么寫 ,怎么達(dá)到在讓你用變長省內(nèi)存的同時 識別你的有效字符 //如果數(shù)組里存utf-8 你想想 別人要以字節(jié)數(shù)讀字符 半個的時候怎么搞 //這跟gdi圖像處理是同一個道理 jpg png 各種是落地格式都可以讀進(jìn)來 但是到內(nèi)存都是bmp //還有不論哪種printf 或者其他接口 都不支持所謂的utf-8的參數(shù) 也沒這種接口可言 //https://zhuanlan.zhihu.com/p/23190549 //前幾天在微博上受到了@belleve給我的啟發(fā),于是簡單地實現(xiàn)了幾個在 windows //下接受 utf - 8 參數(shù)的 printf 系列函數(shù)。大致思路是判斷當(dāng)前 stdout / stderr //是否為控制臺,如果是控制臺則將參數(shù)轉(zhuǎn)為 utf - 16 后調(diào)用 wprintf 輸出,否則不轉(zhuǎn)換直接調(diào)用 printf。 //l 是一個很微妙的 ,稱之為轉(zhuǎn)換為寬字符的字面量 什么叫字面量 根據(jù)你當(dāng)前編程環(huán)境 以及源代碼編碼 轉(zhuǎn)換成對應(yīng)的字節(jié) //l"發(fā)" 字面量 你細(xì)品 setlocale(lc_all, ""); printf("--------------------"); //wchar_t wc = l'破'; std::wstring wstr = l"破a的"; std::cout << wstr.size() << std::endl; //utf-8 只是流行 ,事實上utf-8 一個漢字要占3字節(jié) 而utf-16一個漢字一字節(jié) /*wchar_t wstr2[] = l"破曉s"; wprintf(l"%ls", wstr2);*/ printf("--------------------//"); char utf82[50] = { 0 }; int len = 0; unicodetoutf8(wstr2, utf82, &len); //char* str222 = unicodetoutf8(wstr2); //printf("%s", str222); //printf("aaa"); return 0; // //c++ 中指針的變種 引用的使用方式 printf("aaa\r\n"); int a = 123; int& b = a; a = 456; printf("%d \r\n", b); test1(b); printf("%d \r\n", b); int c = 345; test1(c); printf("%d \r\n", c); return 0; }
關(guān)于c++中的字符串編碼處理的文章就介紹至此,更多相關(guān)c++字符串編碼內(nèi)容請搜索碩編程以前的文章,希望以后支持碩編程!