简 述: “乱码” , 应该是很多人都遇到过的问题。以前总是网上简单搜一下、然后设置一下,然后重启生效,就没有去管了。 对其乱码原理有一些模糊,但是实则是不知道,因为我给其 ta 人解释清楚。直到这次再次遇到了乱码的问题,再一遍的查阅资料之后,,,后面直接解决了。 弄清楚原理之后,就感觉很多写博客都很累赘,但是又没有写清楚。遂自己动手来一篇,希望可以达到言简意赅。

以后博客会写的尽量精简,,,

[TOC]


本文初发于 “偕臧的小站“,同步转载于此。


故事背景:

将所有的软件和资料,全部从 Mac 迁移到 Win 的时候,发现了 git bash 出现了乱码,按照网上只有解决方案、但是没有原理解释的博文一通乱试,无果。于是决定自己自己弄清楚原理,然后自己成功的解决了: Git Bash 在win10 显示中文为数字的解决方案


乱码原因:

用一个小例子开头:

乱码产生的原因,用一个具体的例子解释下: 再文本编辑器中,随手写下一段话,然后按照每 4 个字节大小保存一个字符,将其翻译为 4 * 8 位 个 0 / 1 二进制,保存到内存里面,当按下“保存”的是时候,这一串二进制就存在了硬盘里面; 但在另外一个电脑中打开这个文本文件, 然后将这一串二进制从硬盘读取、加载到内存,然后按照每 3 字节的顺序,将其翻译为对应的字符,最后发现出现全文都是乱码。

这个例子,感觉可以比较的理解乱码原因(虽然不全面、不正确、但是却可以以点破面,便于理解乱码的原因)。


乱码历史原因:

Linux: 一些皆文件

这句话,对于整个计算机来说,都是一样的,所有的文件类型(文本信息、照片、音乐、视频等)都只是 0/1 的二进制段。而将这一段纯 01010101…. 的二进制翻译为人可以看得懂的字符串信息,就叫解码;将人能看得懂的字符串保存为纯二进制,按照某种关系,这就是编码后保存。


计算机最开始只有少数的字母和符号,将其按照一张表格,将每一个字母或者符号找到一个对应的数字,然后以二进制段的形式保存(这个二进制段的长度暂定于 一个字节长度,恰好 128 个),这个映射关系被称为 ASCII 编码。后来随着其他各国的本地语言加入,就扩展这个映射表,前面的128 个 “字母或符号”,还是按照一个字节的长度,还是这个顺序。然后从 129 开始,就按照本国的语言,创建“某汉字”对应“某数字”,然后“某数字”保存到 x 字节中;其他国家也是这样,不过是将 “某汉字” 替换为“某拉丁文”、“某西欧文”等。然后这个 x 字节,随着时间的多次改版,也由2 –> 3 –> 4 –> 2~~4 个字节长度。

中国大陆常用编码:简体中文, GBK

台湾常用:繁体中文, BIG5 (是指代当时台湾五个著名的中文厂商的名称:大千、倚天、國喬、零壹、精業)


这个问题只是解决了本国语言和英文。当时面对另外一个场景,就傻眼了:一篇文献中,同时要引用中文、西欧文、拉丁文、小语种的文字呢??? 于是在 20 世纪 80 年代末,大家坐在一起,决定讨论下这个事情,于是 Unicode 统一码联盟成立。 于 1991.10 发布了 Unicode 1.0, 到了 2012.1.31,发布到了 Unicode 6.1。

需要注意的是:

  • Unicode 是「字符集」,字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point);

  • UTF-8 是「编码规则」, 编码规则:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程);


在 Unicode 尚未成熟的年代, Windows是使用各个国家的多 字节编码,来支持每个国家的语言的。 在 巨硬的 WinNT 发布之后,所有的 API 改用了 UTF-16 编码方式。


Unicode 编码开始只有65536个字符空间。

UCS-2: 恒定用 2 字节表示所有字符。

UCS-4(或UTF-32): 恒定用 4 字节表示所有字符

UTF-16: 当Unicode字符超过65536之后,参考「多字节编码」,通过双字节扩展四 字节的方式表示所有字符

UTF-8: 每一个字符按规则编码 为1~4任一字节组合。中文通常为3字节长。

UTF-8 + bom: 和 UTF-8 的去区别就是,文件开头有 U+FEFF;


总结:

感觉就是一句话,用什么 [编码规则] 保存为何种形式的二进制段,就用同样的[编码规则]来读取。自然就不会出现乱码了。


字符保存等价形式:

当然,自己也可以测试一下

// 因此以下几行等价(以 UTF-8编码保存 C++源文件):
const char s1[] = "最喜欢C++了!"; 
const char s2[] = "\xe6\x9c\x80\xe5\x96\x9c\xe6\xac\xa2\x43\x2b\x2b\xe4\xba\x86\xef\xbc\x81"; 
const char s3[] = { 0xe6, 0x9c, 0x80, 0xe5, 0x96, 0x9c, 0xe6, 0xac, 0xa2, 0x43, 0x2b, 0x2b, 0xe4, 0xba, 0x86, 0xef, 0xbc, 0x81, 0x00 };

参考:


系列地址:

QtExamples

欢迎 starfork 这个系列的 QT / DTK 学习,附学习由浅入深的目录。