首先要来说一说字符集和编码的关系,这两者既有交叉、又有异同。https://www.runoob.com/w3cnote/charset-encoding.html
就非西欧字符而言,比如中国以及港澳台,在任何编程语言的开发中都不得不考虑字符集及其表示。在c++中,对于超过1个字节的字符,有两种方式可以表示:
1、多字节表示法;通常用于存储(空间效率考虑)。
2、宽字符表示法,通常用于程序中(性能考虑)。
目前最主要或最常见的编码应该来说包括:
- ASCII,7位。
- ISO-Latin-1(字符集)/ISO-8859-1(编码),8位。
- UCS-2,16位定长。
- UTF-8,8-32位变长。
- UTF-16,16或32位变长。
- UCS-4/UTF-32,32位定长。
对于特定的字符,各编码格式所占的字节数和编码值如下:
说到UTF-16/UTF-32,不得不说BOM(byte order mark),它的作用跟网络编程中的字节码顺序概念一样,用于标识使用big endian或者little endian。
无BOM的字节流开始:
带BOM的字节流开始:
在c++中,并没有原生支持GBK/GB18050/UTF-8的编码,如下:
基本上广泛用的就是char和wchar_t(默认是UCS-2编码,而c默认是ascii解析,所以一定要设置正确的locale,如setlocale(LC_ALL,"chs"))。
对于常规控制台输入的,基本上网上很多demo了,所以接下去来看下从文件或者网络socket端过来的utf-8或者GBK编码如何处理的。
windows和linux下宽字符处理差异
// c++下常量字符串不能赋值给char指针
const wchar_t* cn_name;
const char* ext;
setlocale(LC_ALL, "chs"); // windows下必须使用chs,不能使用zh_CN.GBK,很多网上的示例是不正确的如https://wenku.baidu.com/view/40b0a0a672fe910ef12d2af90242a8956becaaac.html、javascript:void(0)。
t.ext = "ext name";
t.cn_name = L"中文扩展名balaba";
printf("ext=%s, cn_name=%ls\n",ext, cn_name); // ok 只有同时输出宽字符和标准字符才有意义,分开无意义
wprintf(L"cn_name=%s,ext=%s\n", cn_name, ext); // ext not ok
所以除非特殊需求,日常情况下使用printf格式掩码ls即可满足日常需求。
如果是跨平台编译,一定要确保原文件为UTF-8编码,不然windows下没有问题,到了linux下会报“error: converting to execution character set: Invalid or incomplete multibyte or wide character”,虽然加编译选项"-finput-charset=GBK"可以解决,但是并非上策。