查看本文前先弄清字符编码的大概含义,可点击本链接
本文参考链接
VS为什么会出现中文乱码(解决方案一)
VS为什么会出现中文乱码(解决方案二)
VS为什么会出现中文乱码(解决方案三)
VS为什么会出现中文乱码(解决方案四)
查看控制台编码
编码对应标识符
操作系统编码(一)
操作系统编码(二)
VS 命令行
更改执行字符集
bom
utf-8, wstring,ansi
文件读取
宽窄字符转换
VS 字符从输出到输出
调试窗口
Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加有效地运行。
Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数
Windows 98 只支持ANSI,只能为ANSI开发应用程序。
Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。
调试窗口
源码字符集编码:默认VS文件->高级保存选项->编码中可以查看设置当前源代码文件的源码字符集。向文件中写入数据时也是使用该编码写入。可通过XXX(待验证方式进行修改)。
执行编码:默认与操作系统相同,中文的Windows系统的编码为GB2312(兼容ASCII、GBK)。若文件带有bom,会根据bom将文件转换成编译器执行字符集(执行编码)后进行编译。
解析字符集:设置控制台程序输出代码页,默认与操作系统相同,通过"SetConsoleOutputCP(编码标识符);"语句可更改。用cin让用户输入时,却不能正确输出用户输入的中文字符。
可能是因为cout有自己的解析字符集,不会随着chcp命令改变。
我们运行的可执行文件并不是我们口中的那个CMD(按Win+R、cmd打开的那个CMD)。
- 我们代码的编码是UTF-8 源码字符集
- 把UTF-8编码的代码交给mingw,它也默认当做UTF-8处理(目前为止这是正确的)
- mingw处理后生成的数据还是UTF-8编码(目前为止还是正确的)执行编码
- 把mingw处理后的数据(UTF-8编码)给cmd(目前为止也是正确的)
- cmd按GBK编码处理它(UTF-8),这时出现错误,所以出现乱码 解析字符集
解决办法::
更改步骤4修改mingw的输出,让它在编译时使用GBK编码输出,然后再让CMD去显示。将窄字符的编码方式改为GBK
VS为什么会出现中文乱码(解决方案三)
第一个参数指定窄字符或窄字符串的字面值常量的内部编码方式,默认为UTF-8。例如指定此选项为GBK,则窄字符或窄字符串常量将会以GBK编码方式存储而不是默认的UTF-8编码方式。
第二个参数,可能不需要加,加了第一个参数后还有乱码可以试试这个,意思是指定源文件的文件编码。
验证:
按照解决方案一:
- 若文件是GBK(ANSI)编码,执行编码和解析编码均和操作系统相同(GB2312)。则读取ANSI编码的文件时,不论debug还是控制台输出都不会乱码。验证结果是对的。
- 若文件时UTF-8编码,执行编码和解析编码是GB2312。则读取UTF-8编码的文件时,编译器会将带有bom的文件转化为GB2312,则debug时不应该出现乱码(若文件不带bom则会出现乱码,解决办法是使用带bom的文件可解决编译期间的乱码问题)待验证。但是控制台输出会出现中文乱码。若更改控制台程序输出方式则可以解决该问题。更改方式是system(“chcp 65001”);。这种情况下cout依旧无法输出中文乱码,原因未知。
- 评论区有人指出使用"SetConsoleOutputCP(编码标识符);"方法,
- 解决方案三对更改控制台编码方式解决中文乱码的问题有不一样的想法。方案三任务VS的控制台和我们改变编码方式的那个控制台不是同一个。待验证
- 根据解决方案四可知,窄字符在输出到控制台时是不会进行编码转换的,所以输出窄字符需要更改控制台的编码。输出宽字符时,会根据setlocale()设置的区域信息进行输出。待验证
locale
locale对象封装了一组特定于区域性的功能,程序可以使用这些功能来增强国际可移植性(有关更多信息,请参阅标题<locale>)。
在构建locale对象时,本地化引擎初始化与它关联的所有facets(facet是描述与特定文化方面关联的locale feature的类。),并将其提供给程序。
每个程序都有一个locale对象,这是其全局locale, 默认是
classic locale。可以通过调用locale::global来更改。此全局locale由所有 default-constructed 的locale对象构成。
全局locale还影响C locale(请参阅函数setlocale):当使用locale::global设置新的命名全局locale时,C locale也会被修改。
locale对象可用于访问其关联facet,以使用其格式化功能。它们也可以通过调用流的imbue()成员函数单独注入特定的流对象(如cin、cout或文件流)。
setlocale函数
作用: 设置要由当前程序使用的区域设置信息,更改整个区域设置或部分区域设置。该函数还可用于通过传递NULL作为参数区域设置的值来检索当前区域设置的名称。
区域设置包含有关如何解释和执行某些输入/输出和转换操作的信息,同时考虑到特定的位置和语言设置。
大多数运行环境都有根据用户偏好或本地化设置的某些区域设置信息。但是为了独立于系统区域设置,在开始时,所有C程序都有“C” locale,这是一个相当中立的区域设置,区域设置信息最少,使程序的结果可以预测。为了在环境中使用默认区域设置,可以使用setlocale(LC_ALL,"")。
程序启动时,选择的区域设置是“C”区域设置,与调用setlocale(LC_ALL,“C”) 时设置的区域设置相同。
设置字符编码
C/C++ 语言标准中定义了其运行时的字符编码为 “C” ,“C” 即ASCII 字符集的一个子集,使用setlocale会改变整个应用程序的编码方式。
语法: string setlocale(string category, string locale);
作用: setlocale函数用来配置地域的信息。
参数 category :
官方文档
value | 受影响locale的部分 |
---|---|
LC_ALL | 整个locale设置。 |
LC_COLLATE | 影响strcoll和strxfrm的行为 |
LC_CTYPE | 影响字符处理函数(< cctype >的所有函数,除了 isdigit 和 isxdigit),以及多字节和宽字符函数 |
LC_MONETARY | 影响localeconv返回的货币格式信息 |
LC_NUMERIC | 影响格式化输入/输出操作和字符串格式化函数中的小数点字符,以及localeconv返回的非货币信息 |
LC_TIME | 影响strftime的行为 |
参数 locate :
包含C区域设置名称的C字符串。这些是特定于系统的,但至少必须存在以下两个区域设置:
locale name | description |
---|---|
“C” | Minimal “C” locale |
“” | Environment’s default locale |
如果此参数的值为NULL,则函数不会对当前区域设置进行任何更改,但函数仍然返回当前区域设置的名称。
单字符宽字符互相转换
将char转换为wchar_t
mbstowcs函数
语法: size_t mbstowcs (wchar_t* dest, const char* src, size_t max);
作用: 将多字节字符串转换为宽字符字符串
将src指向的多字节序列转换为等效的宽字符序列(存储在dest指向的数组中),直到最大宽字符被翻译或多字节序列src中遇到空字符(该序列也被翻译和存储,但不计入函数返回的长度)。如果成功翻译了最大字符,则存储在dest中的结果字符串不会为空终止。
此函数的行为取决于所选C locate设置的LC_CTYPE类别。
返回值: 写入dest的宽字符数,不包括最终终止的空字符。如果遇到无效的多字节字符,则返回值(size_t)-1。请注意,size_t是unsigned integral 类型,因此可能返回的值都不小于零。
参数dest:
dest是指向wchar_t元素数组的指针,它足够长,可以包含生成的序列(最多是最大宽字符)。
参数src:
被解释的多字节序列。多字节序列应以初始移位状态开始。
size_t max:
写入dest的最大wchar_t字符数。Size_t是unsigned integral 类型。
将wchar_t转化为char
wcstombs函数
语法: size_t wcstombs (char* dest, const wchar_t* src, size_t max);
作用: 将宽字符字符串转换为多字节字符串
将src指向的宽字符序列转换为多字节等效序列(存储在dest指向的数组中),直到最大字节被翻译或宽字符转换为空字符。如果成功翻译了最大字节,则存储在dest中的结果字符串不会为空终止。生成的多字节序列以初始移位状态(如果有的话)开始。此函数的行为取决于所选C locate设置的LC_CTYPE类别。
/* wcstombs example */
#include <stdio.h> /* printf */
#include <stdlib.h> /* wcstombs, wchar_t(C) */
int main() {
const wchar_t str[] = L"wcstombs example";
char buffer[32];
int ret;
printf ("wchar_t string: %ls \n",str);
ret = wcstombs ( buffer, str, sizeof(buffer) );
if (ret==32) buffer[31]='\0';
if (ret) printf ("multibyte string: %s \n",buffer);
return 0;
}
输出:
wchar_t string: wcstombs example
multibyte string: wcstombs example
将char转换为wchar_t
mbtowc函数
语法: int mbtowc (wchar_t* pwc, const char* pmb, size_t max);
作用: 将多字节序列转换为宽字符
pmb指向的多字节字符转换为wchar_t类型的值,并存储在pwc指向的位置。该函数返回多字节字符的长度(以字节为单位)。
参数pwc:
指向wchar_t类型的对象的指针。此参数可以是一个空指针,在这种情况下,函数不存储wchar_t翻译,但仍然返回多字节字符的长度(以字节为单位)。
参数pmb:
指向多字节字符的第一个字节的指针。此参数可以是一个空指针,在这种情况下,函数将其内部偏移量重置为初始值,并返回该多字节字符是否具有与状态相关的编码。
max:
多字节字符pmb的最大字节数。在任何情况下,检查的字符不得超过MB_CUR_MAX。
MB_CUR_MAX:
多字节字符的最大大小,此宏扩展到type size_t 的正整数表达式,其值是当前区域设置(类别 LC_CTYPE)的多字节字符中的最大字节数。其值永远不会大于MB_LEN_MAX(见climits)。
返回值:
如果作为pmb传递的参数不是空指针,则返回写入pmb的字符的大小(以字节为单位)。如果没有字符对应关系,则返回-1。如果作为pmb传递的参数是空指针,如果多字节字符编码与状态相关,函数将返回非零值,否则返回零值。
/* mbtowc example */
#include <stdio.h> /* printf */
#include <stdlib.h> /* mbtowc, wchar_t(C) */
void printbuffer (const char* pt, size_t max)
{
int length;
wchar_t dest;
mbtowc (NULL, NULL, 0); /* reset mbtowc */
while (max>0) {
length = mbtowc(&dest,pt,max);
if (length<1) break;
printf ("[%lc]",dest);
pt+=length; max-=length;
}
}
int main()
{
const char str [] = "mbtowc example";
printbuffer (str,sizeof(str));
return 0;
}
Printbuffer按字符打印多字节字符串字符。
输出:
[m][b][t][o][w][c][ ][e][x][a][m][p][l][e]
将wchar_t转化为char
wctomb函数
语法: int wctomb (char* pmb, wchar_t wc);
作用: 将宽字符转换为多字节序列
宽字符wc被转换为多字节,并存储在pmb指向的数组中。该函数返回调用后pmb指向的等效多字节序列的长度(以字节为单位)。
wctomb有自己的内部偏移量,只有通过调用此函数才能根据需要进行更改。pmb使用空指针对函数的调用重置状态(并返回多字节序列是否与状态有关)。此函数的行为取决于所选C locate设置的LC_CTYPE类别。
参数pmb:
指向足以容纳多字节序列的数组的指针。当前区域设置中字符的多字节序列的最大长度为MB_CUR_MAX字节。可以使用空指针调用函数,在这种情况下,函数将其内部移位状态重置为初始值,并返回该多字节字符是否具有与状态相关的编码。
wc:
Wchar_t类型的宽字符。
返回值:
如果作为pmb传递的参数不是空指针,则返回写入pmb的字符的大小(以字节为单位)。如果没有字符对应关系,则返回-1。如果作为pmb传递的参数是空指针,如果多字节字符编码与状态相关,函数将返回非零值,否则返回零值。
/* wctomb example */
#include <stdio.h> /* printf */
#include <stdlib.h> /* wctomb, wchar_t(C) */
int main() {
const wchar_t str[] = L"wctomb example";
const wchar_t* pt;
char buffer [MB_CUR_MAX];
int i,length;
pt = str;
while (*pt) {
length = wctomb(buffer,*pt);
if (length<1) break;
for (i=0;i<length;++i) printf ("[%c]",buffer[i]);
++pt;
}
return 0;
}
输出:
[w][c][t][o][m][b][ ][e][x][a][m][p][l][e]
宽、窄字符转换总结
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); //转换单字符串为宽字符串
size_t wcstombs(char *mbstr, const wchar_t *wcstr, size_t count ); //转换宽字符串为单字符串
#include <stdlib.h>
CString str = L"hello";
char sss[20];
wcstombs(sss,str.GetBuffer(),20); //转换宽字符为单字符
// stl 宽字符到 单字符转换
wstring str = L"Hello";
std::wstring::size_type len = str.length();
std::string s(len*2,0);
size_t total = wcstombs(&s[0],str.c_str(),len*2);
s[total] = '/0';
return s;
//mbtowc 和 wctomb 是单个字符相互转换
int len;
setlocale (LC_ALL, "chs"); //设置为简体中文环境
wchar_t wc = L'中';
wprintf(L"1个宽中文字符:%c /n",wc);
char* p = "中";
len = mbtowc (&wc, p, MB_LEN_MAX);
wprintf(L"单字符串转换为1个宽字符:%c 长度: %d/n",wc,len);
char pcmb[MB_LEN_MAX];
len = wctomb (pcmb, wc);
pcmb[len] = 0;
printf("宽字符转换为单字符串:%s 长度:%d/n",pcmb,len);
//utf8 字符串
BYTE utf8[1024];
wchar_t wstr[1024];
char mstr[1024];
//UTF-8转换为宽字符
MultiByteToWideChar( CP_UTF8, 0, utf8,1024, wstr, sizeof(wstr)/sizeof(wstr[0]) );
//宽字符转化为多字节
WideCharToMultiByte( CP_ACP,0,wstr,-1,mstr,1024,NULL,NULL );
注:mbstowcs()是C库函数,要正确的设置Locale才能进行转换,MultiByteToWideChar()是win32函数
为什么使用mbstowcs()一定要调用 setlocale 呢?
在 C/C++ 语言标准中定义了其运行时的字符集环境为 “C” ,也就是ASCII 字符集的一个子集,那么 mbstowcs 在工作时会将 cstr 中所包含的字符串看作是ASCII 编码的字符,而不认为是一个包含有 chs 编码的字符串,所以他会将每一个中文拆成 2 个 ASCII 编码进行转换,这样得到的结果就是会形成 4 个 wchar_t 的字符组成的串。
如何才能够让 mbstowcs 正常工作呢?
在调用 mbstowcs 进行转换之间必须明确的告诉 mbstowcs 目前 cstr 串中包含的是 chs 编码的字符串,通过 ==setlocale( LC_ALL, “chs” ) ==函数调用来完成。
需要注意的是这个函数会改变整个应用程序的字符集编码方式,必须要通过重新调用 setlocale( LC_ALL, “C” ) 函数来还原,这样就可以保证 mbstowcs 在转换时将 cstr 中的串看作是中文串,并且转换成为 2 个 wchar_t 字符,而不是 4 个。
如果不使用setlocal就会出现中文乱码的问题,如下所示:
mbstowcs()与MultiByteToWideChar()的区别
mbstowcs()是MultiByteToWideChar()的简化版,在中文字符的转换上,前者似乎无法对中文字符进行处理,转换后显示成乱码,而后者则不会。
WCHAR strPath[MAX_PATH];
CHAR buf[] = “i am 中国人”;
// 简化版对中文字符的转换不好
// mbstowcs( strPath, buf, MAX_PATH );
// 还是这个比较好
MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf), strPath, MAX_PATH );‘’‘