Windows下c++字符编码(⼆)
编译器对c++源⽂件编码的识别
注意,这⾥说的,是对于源⽂件编码的识别,⽽不是⽤c++来读写⽂件。实际上,不同编译器对于源⽂件编码的识别时有差异的,这是你需要特别注意的⼀点。如果你在 Code Blocks ⾥边在源⽂件中写了中⽂,⽤ sublime 打开却发现出现乱码,本⽂可以彻底解决这个问题。
基本知识和基本⼯具
本篇是整理时加上的⼀节,我觉得最好将⼀些基础知识提前说清楚,便于之后的陈述。
⽂件编码基础知识
前⾯说完了基本的编码知识,具体在⽂件中是这么实现的呢。
⽂本⽂件
通常意义上理解的⽂本⽂件,就是只保存字符的编码信息,⽽不保存字符的字体、⼤⼩、颜⾊等信息的⽂件。事实上,所有的编程源⽂件都是⽂本⽂件。
在windows下我们新建⼀个⽂本⽂件,⼀般默认是使⽤Notepad这个软件,默认编码是ANSI。不过如果你采⽤其他软件,例如
Notepad++,vim,或者VS Code,结果可能是不尽相同。但是⽆论如何,字符在⽂件中保存,⼀定还是0101这样的编码(计算机上什么东西不是这样编码的呢)。
ANSI
这个基本上没有什么好说的,windows下使⽤记事本(Notepad)新建⼀个⽂本⽂件,就是ANSI编码的。其在硬盘上保存的,就是这些字符的ANSI编码。
utf-8
现在有⼀个问题,同样⼀个.txt⽂件,编辑器如何判断它是什么编码呢。所以很多⽂件都有⼀些打头的⼀些字节来表⽰⾃⼰的⽂件类型。⼀些utf-8⽂件就会有⽂件头,称为BOM,utf-8⽂件的BOM是三个字节:0xef,0xbb,0xbf。不过⼀些由于utf-8编码的普及,现在⽆BOM的utf-8⽂件更多。
作为utf-8拥护者,我们⼀般推崇⽆BOM的utf-8⽂件
⽐如 sublime 识别⽂件默认就是utf-8,如果⽂件编码是utf-8,就会正确显⽰,如果⽂件实际上是ANSI编码,显然不会有BOM,sublime 还是把他当成utf-8⽂件来读,就会产⽣乱码。
utf-16⽂件
前⾯说过,utf-16实际上有两种存储⽅式,⼤端序和⼩端序,这关系到⼀个utf-16字符两个字节哪个在硬盘中位置靠前。这个信息要告诉编辑器,就依靠⽂件头了:
- ⼤端序utf-16BE,⽂件头为0xfe,0xff。
- ⼩端序utf-16LE,⽂件头⽂0xff,0xfe。
utf-32⽂件
⽬前我的⼯具⽆法查看utf-32⽂件,就不管了,⼀般来说utf-32⽂件不会⽤到。
⼯具
Notepad++
我的主要⼯具就是Notepad++了,它会判断⽂件编码,可以很⽅便地转换编码,虽然我现在常⽤VS Code,不过写此⽂
时,Notepad++帮了⼤忙,如果你碰到乱码⽂本⽂件,可以⽤他来查看。
cmd(命令提⽰符)
要特别注意的是,命令提⽰符的输⼊输出默认编码是ANSI,所以任何输出字节都会按照ANSI解码,然后再打印出来。所以,通常只有ANSI编码字符串才能正确输出,如何输出其他编码字符,是后⾯的课题。
python3的str的默认编码是utf-8,其可以正确输出是python⾃⼰实现的。
c++字符串的基本知识
字符初始化
类型名单元初始化(注意前缀)编码
std::string char std::string s = "你好世界";系统默认
std::string char std::string s = u8"你好世界";utf-8
std::u16string char16_t std::u16string s = u"你好世界";utf-16
std::u32string char32_t std::u32string s = U"你好世界";utf-32
std::wstring wchar_t std::wstring s = L"你好世界";系统依赖wchar_t的长度
sizeof(wchar_t)==2;//Winodws下
sizeof(wchar_t)==4;//Unix/Linux下
编译器对源⽂件的识别
实验使⽤的编译器有visual studio 2017的cl.exe(x86_x64),clang 7.0.0(⾃⼰编译的release版本),以及gcc 8.1.0 (MinGW-W64),全部都在命令⾏下编译,实验时会具体说明。所有实验源⽂件编码也会标注。
#include<iostream>
#include<string>
// encoding = utf-8 (no BOM)
int main(){
std::string sa ="你好世界";
std::string sb = u8"你好世界";
std::cout <<"len(sa)="<< sa.size()<<' '<< sa << std::endl;
std::cout <<"len(sb)="<< sb.size()<<' '<< sb << std::endl;
}
输出:
g++ or clang-cl
len(sa)=12 浣犲ソ涓栫晫
len(sb)=12 浣犲ソ涓栫晫
<
len(sa)=12 浣犲ソ涓栫晫
len(sb)=18 娴g姴銈芥稉鏍 櫕
如果上述源⽂件改成ANSI编码,结果是:
g++
len(sa)=8 你好世界
len(sb)=8 你好世界
clang-cl
test.cpp(6,23):  warning: illegal character encoding in string literal [-Winvalid-source-encoding]
std::string sa = "<C4><E3><BA><C3><CA><C0><BD><E7>";
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp(7,25):  error: illegal character encoding in string literal
std::string sb = u8"<C4><E3><BA><C3><CA><C0><BD><E7>";
<
len(sa)=8 你好世界
len(sb)=12 浣犲ソ涓栫晫
分析:从上诉结果可以看出,
gcc和clang都是默认utf-8编码的,不过,g++没有编码检查,会把任何⽂件都当作utf-8⽂件来读取。
gcc和clang的u8前缀是不起作⽤的,毕竟本来就默认是utf-8编码。
cl 默认编码是ANSI,所以u8前缀是起作⽤的,但是对于⽆BOM的utf-8⽂件,它会当成ANSI来读取,所以它把utf-8的"你好世界"含有的12字节当成了6个ANSI字符,并对其进⾏的转码,试图转为6个utf-8字符,也就是18字节,这显然是越搞越乱。
不过还是可以夸夸cl的,如果你⽤其他编码实验,对于cl,它都会给出:
len(sa)=8 你好世界
len(sb)=12 浣犲ソ涓栫晫
这是由于它会⾃动把他转换为ANSI编码,然后再编译,包括有BOM的utf-8⽂件也是这样的(显然,有
BOM就是告诉了它这是⼀个utf-8⽂件,⽆BOM它就当是ANSI了)。
notepad++⽽对于gcc和clang,其中ANSI和utf-8都会按照utf-8⽂件来读取,但是对于clang,string中的⾮utf-8字符会报错。⽽其他编码,gcc和calng都不⽀持,不过,gcc会报出⼀系列很难看懂的错误,但是clang会贴⼼地告诉你:
fatal error: UTF-16 (LE) byte order mark detected in 'test.cpp', but encoding is not supported
1 error generated.
总结如下
<默认编码是ANSI,它会将其他⽂件先转化成ANSI编码,再编译。但是对于⽆BOM的utf-8⽂件,会当成ANSI从⽽可能出错。
gcc和clang都默认utf-8,clang拒绝ANSI的string(不过注释可以),gcc会把ANSI也当成utf-8,其他编码源⽂件都不⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。