聊聊开发中的编码问题
本文是一篇面向计算机小白的科普文。
很多新手在刚刚接触 C 或者 C++ 的打印语句时,都会尝试向其中添加中文,而后看到控制台出现一堆乱码......
下文会和大家仔细介绍问题的由来。
编码问题的产生
计算机当中的数据使用二进制存储,而作为现代计算机使用者的我们,却几乎没有和二进制打过交道,编码在其中占据了重大的功劳。
其实编码这个词听起来很高级,但并不是一个复杂的概念
如果我现在规定 A
等于 \(1\) 、B
等于 \(2\) ,以此类推,数据 1 16 16 12 5
就可以被解码为 A P P L E
。
上方的例子中,规定的过程就是编码的过程,而将一堆数据还原成单词就是解码的过程。
英文编码
众所周知,计算机最早发明于英语国家,所以全球的计算机中,英文都遵循着一套叫做 ASCII
的编码规则,当然 ASCII
这套规则不仅仅适用于英文字母,同时也适用于数字和各类控制符号。
下方贴出 ASCII
表:
其他语言编码
接下来可以讲讲非英文语言的编码问题了,还记得我们一开始提到的场景吗?我们使用 printf
或者是 cout
语句向控制台输出中文,而控制台输出了一堆乱码,这个其实是一个历史遗留问题,以下我们只介绍中文的场景,其他场景其实和中文并没有本质区别。
早期计算机编码
在现代计算机发明初期,电脑的用户是很少的,而且并没有频繁的跨语言交流需求,于是几乎每个国家都为自己国家的语言制定了一套编码标准。
中文最早的编码标准是 GB2312
,而后由于早期的编码标准收入的字太少(中文博大精深),又陆续推出了 GBK
和 GB18030
的编码标准,它们都是对前者的扩充,也对前者充分兼容。
与此同时,其他国家在没有任何交流的情况下都纷纷推出了自己的编码标准,Windows
系统也针对了各国的编码做了语言的适配,这乍一听是个百花齐放的好事,但是很快问题就产生了。
还是举个例子,我规定了 A
是 \(1\) ,而另一个人规定 Z
是 \(1\) :我们知道,计算机是只存储数据的,也就是只存储了 \(1\) ,那么同样是数据 \(1\) ,在两个人眼中显示的结果就会完全不一样。
现代计算机编码
为了解决计算机在全球跨文化交流中的需求,Unicode
组织推出了名为 Unicode
的全球码,它几乎收录了全球所有文化的字符,大家各种语言的符号组合在一块终于不会乱码了。
Unicode
的现行常用编码格式为 UTF-8
,我们而今使用的几乎所有文本编辑器都会默认使用 UTF-8
进行编码。
至此,问题解决了,吗?
可不要忘记了,Windows
的系统编码是基于早期的计算机编码,微软公司称这种编码为 ANSI
编码(这种编码会根据地区适应编码规则)。
而我们在代码文件中所写的所有的非英文字符都使用的是 UTF-8
编码,我们现在终于可以解释最开始的问题了!
因为我们代码的文件是 UTF-8
编码,而大陆地区Windows系统的默认编码是 GBK
编码,自然就会产生乱码。
解决方案
既然知道了问题,解决的方案就很简单了,我们有两个思路:
- 把代码文件的
UTF-8
编码改成控制台的GBK
编码 - 把控制台的
GBK
编码改成代码文件的UTF-8
编码
方案一
在通常情况下,代码的编码显示在右下角,单击以后手动修改为 GBK
,保存即可。
方案二
在代码的开头添加:
// 你的头文件
#include <windows.h>
int main(){
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
// 你的代码
}
以上代码可以强制将控制台编码修改为 UTF-8
。
其他问题
由于本篇文章写给小白,并没有提及宽窄字符等问题,也没有详细介绍控制字符和 Windows
平台下的路径乱码问题,交由在未来的开发过程中自行探索。
顺带一提
其实中文乱码的问题主要是 Windows
平台,因为它具有大量的全球用户,具有较重的历史包袱,需要对老版本系统的文件兼容。
而 Linux
和 Mac OS
已经强制系统全部使用 UTF-8
编码,所以此类问题并不会出现在这两个系统上。