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