【零基礎學QT】【037】C++中char,wchar,string,wstring

char和wchar_t

  • char是C++基本類型,佔一個字節(8位),可用來存儲單個字符的Unicode碼,由於正好8位,也可用來存儲字節
  • wchar_t是C++擴展類型,佔兩個字節,可以用來存儲Unicode編碼(UTF16)的字符。wchar實質上是unsigned short的一個別名,由於unsigned short佔兩個字節(16位),正好可以用來存放雙字節字符

	typedef unsigned short wchar_t

  • 爲區分普通char,wchar字面量前面會加一個L,表示這是寬字符
  • 雖然wchar_t本質上只是unsigned short的別名,但是儘量別將它們混用,一來不易理解,二來編譯器會將二者做一些區分處理,所以二者並不是完全一樣的,只是底層字節長度一樣

char數組和wchar_t數組的使用
C++中沒有字符串類型,通過char數組來表示字符串,C++會在字符數組的最後拼加一個’\0’字符,來標記字符串結束
'\0’字符對應的ASCII碼爲0,在char數組中,結束符佔一個字節,數組長度即爲字符數+1,在wchar數組中,結束符佔兩個字節,數組長度即爲字符數+2
char數組既可以通過char[]來表示,也可以通過char*來表示,因爲char的長度是已知的,初始指針的地址加1就可以拿到下個char的地址


	//包含'\0'在內共6個字符(6個字節)
    char str[] = "hello";
    cout << sizeof(str) << endl;
    for (auto ch : str)
        cout << ch << endl;

	//包含'\0'在內共6個字符(6個字節)
    char* pStr = "hello";
    cout << strlen(pStr) << endl;
    cout << pStr[0] << endl;
    cout << pStr[5] << endl;
    int next = 0;
    while (pStr[next])
        cout << pStr[next++] << endl;


    //包含'\0'在內共6個字符(12個字節)
    wchar_t str[] = L"hello";
    wcout << sizeof(str) << endl;
    for (auto ch : str)
        wcout << ch << endl;

    //包含'\0'在內共6個字符(12個字節)
    wchar_t* pStr = L"hello";
    wcout << wcslen(pStr) << endl;
    wcout << pStr[0] << endl;
    wcout << pStr[5] << endl;
    //wchar_t*指針做++運算時,不是按字節算的,而是字符索引
    int next = 0;
    while (pStr[next])
        wcout << pStr[next++] << endl;

char保存寬字符會發生什麼


	//保存45的二進制字節
    char a = 45;
    //字符對應的ASCII碼爲45,保存ASCII碼的二進制字節,即二進制的45
    char b = '-';
    //20013佔兩個字節,低字節部分爲45,只保存低字節部分字節,即二進制的45
    char c = 20013;
    //寬字符對應的Unicode/UTF16編碼爲20013,只保留低字節部分,,即二進制的45
    char d = L'中';
    //char打印時,將內存中的字節視爲ASCII碼編號,打印對應的字符
    //由於a,b,c,d內存字節都是一樣的,打印出來的字符都是一樣的,即b字符
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    cout << d << endl;

雖然char保存寬字符是不合理的,但是C++卻允許進行強制轉換
因爲類型安全檢測,是一個很耗性能的工作,C++放棄了易用性來換取性能
我們講解這些,是爲了加強對C++底層原理的理解,用char去保存寬字符並沒有實際的應用意義,不要在實際應用中這樣去使用

本地編碼
字符串存儲到本地,必須要轉換爲字節,而不同的編碼方式,將會轉換出不同的字節
那麼,編譯器到底會以哪種編碼方式來編譯我們代碼中的文本?
我們可以通過以下代碼,來告訴編譯器,使用指定的字符編碼方式

	
	//獲取操作系統默認區域信息
	//如指定區域名稱,則獲取指定區域的信息
    locale locale = std::locale("");
    //設置全局區域信息
    //C++編譯器將根據區域信息,使用對應區域默認的字符編碼
    locale::global(locale);

如果我們沒有明確指定區域信息,編譯器將根據本地環境設置一種默認編碼方式,所有文本都將被視爲這種編碼
這裏的本地環境,可能包括編譯器,代碼編輯器(IDE),文件編碼,操作系統等多種因素
不同的編譯器可能使用不同的默認編碼,IDE又可能會根據軟件設定,源代碼文件的編碼,操作系統默認語言設定等因素,來修改編譯參數
我們把這種不確定的,有本地諸多因素最終決定的編碼方式,叫做本地編碼

char*存放寬字符會發生什麼

	
	//這裏使用的本地編碼是UTF8
	//UTF8中每個'中'字符佔三個字節,4個字符共12個字節
    char* e = "中中中中";
    //編譯器直接將UTF8編碼對應的字節,從e指向的地址開始,寫入內存
    int next = 0;
    while (e[next] != 0)
        cout << (int) e[next++] << endl;
    //編譯器並不知道char*是從string轉換來的,所以不可能爲char保留編碼信息
    //當我們通過cout來打印char*時,只會以ASCII碼或Unicode編碼(UTF16)逐個打印字符,直到遇到'\0'
    //顯然,用char*保存中文字符串也是沒實際意義的,因爲保存和讀取時的編碼可能不一致,肯定會亂碼
    cout << e << endl;

	//打印結果如下
	//可以看出,保存時,是每個字符按UTF8用3個字節保存的
	//讀取時,每兩個字節作爲一個Unicode/UTF16字符來打印
	[-28,-72,-83,-28,-72,-83,-28,-72,-83,-28,-72,-83]
	['涓','鏜','腑','涓','鏜','腑']

	//當一個字符串中,既包含英文字符,又包含中文字符時
	//cout將以0開頭的字節視爲ASCII碼,非0開頭的字節,和之後的字節,視爲Unicode碼(UTF16)
	//測試代碼
	e = "A中B中C中D中EEEE";
	cout << e << endl;
	['A','涓','瑽','涓','瑿','涓','璂','涓','璄','E','E','E']

string和wstring
string和wstring是C++標準庫中的字符串封裝類,分別對應char和wchar類型的字符
現代C++代碼中,一般都使用這兩個作爲字符串處理類,而不是char[]和wchar[]


    string str = "hello";
    cout << str << endl;
    cout << str[0] << endl;
    cout << (int) str[5] << endl;
    cout << str.length() << endl;


    //設置本地編碼,自動獲取操作系統設定
    //ACP:ANSI-Code-Page,國家標準代碼頁
    setlocale(LC_ALL, ".ACP");
    //也可以直接指定國家區域和語言
    setlocale(LC_ALL, "Chinese-Simplified");

	//記得加上以上聲明,否則可能亂碼,或者wcout不輸出字符
	//這是C++原生代碼中,使用中文字符串的標準寫法
    wstring str = L"我是中國人";
    wcout << str << endl;
    wcout << str[0] << endl;
    wcout << (int) str[5] << endl;
    wcout << str.length() << endl;

string和wstring轉換


	#include <iostream>
	#include "windows.h"
	
	using namespace std;
	
	string wcharToString(const wchar_t* pWchar) {
	    int length = WideCharToMultiByte(CP_ACP, 0, pWchar, -1, NULL, 0, NULL, NULL);
	    char* pChar = new char[length];
	    WideCharToMultiByte(CP_ACP, 0, pWchar, -1, pChar, length, NULL, NULL);
	    pChar[length - 1] = 0;
	    string str(pChar);
	    delete[] pChar;
	    return str;
	}
	
	string wstringToString(const wstring& rWstring) {
	    return wcharToString(rWstring.c_str());
	}
	
	wstring charToWstring(const char* pChar, int nLen) {
	    int length = MultiByteToWideChar(CP_ACP, 0, pChar, nLen, 0, 0);
	    wchar_t* pWchar = new wchar_t[length + 1];
	    MultiByteToWideChar(CP_ACP, 0, pChar, nLen, pWchar, length);
	    pWchar[length] = 0;
	    //跳過OxFEFF
	    if (pWchar[0] == 0xFEFF)
	        for (int i = 0; i < length; i++)
	            pWchar[i] = pWchar[i + 1];
	    wstring wstring(pWchar);
	    delete[] pWchar;
	    return wstring;
	}
	
	wstring stringToWstring(const string& rString) {
	    return charToWstring(rString.c_str(), rString.size());
	}
	
	int main(int argc, char* argv[]) {
	    
	    wstring a = L"ABCDEFG";
	    string b = wstringToString(a.c_str());
	    cout << b << endl;
	
	    string c = "ABCDEFG";
	    wstring d = stringToWstring(c.c_str());
	    wcout << d << endl;
	
	    return 0;
	}

string和wstring存放中文時的區別

  • string以本地ANSI碼存儲字節,但是讀取時將字節視爲ASCII/Unicode(UTF16)
  • wstring以Unicode碼(UTF16)存儲字節,讀取時也將字節視爲Unicode碼(UTF16)
  • 所以string存儲寬字符會亂碼,而wstring則不會

對不同編碼方式不清楚的請看這裏:https://hellogoogle.blog.csdn.net/article/details/103455674

發佈了429 篇原創文章 · 獲贊 43 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章