【零基础学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

发布了442 篇原创文章 · 获赞 45 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u013718730/article/details/103469251