谈谈字符编码问题

最近在实验室编写文件,遇到一件奇怪的事情,在自己的笔记本上能正常编码,却在学校的电脑上编码总是乱码,于是经历种种磨难,算是弄明白了一点点,下面简单介绍一下字符编码的知识(由于编码种类繁多,我只简单的提一下,其余自己百度,文末有相关链接)

常见的字符集:

​ 百度百科定义: 字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。中文文字数目大,而且还分为简体中文和繁体中文两种不同书写规则的文字,而计算机最初是按英语单字节字符设计的,因此,对中文字符进行编码,是中文信息交流的技术基础。

​ 下面介绍几个常见的字符集:ASCII字符集、GB2312、 GBK、utf-8、utf-16、utf-32

​ 所谓字符集就是按一定的规则去解释或者翻译0 1 代码的,有点像翻译表或者对照表。

ASCII 以及EASCII

​ 首先,我们要明白计算机内部所有东西都是以10来表示的,刚开始我们用8位0 1代码进行表示,一共有2^8= 256种状态,由于当时计算机很昂贵并且很大,美国人只有用了127个字符就表示了英语中所有的字符,这就是ASSIC码,从此ASSIC码诞生了,

并用后面的128~256号状态来表示西欧其他国家的字符,于是把0~256号状态的所有字符统称为扩展的ASCCI字符(即EASCII),即最高位为0 代表0~127号字符,最高位为1 是后面扩展的字符集,记住,此时计算机始终按照8位二进制进行编码.

GB2312 、GBK 、GB 18030

​ 等到了中国,发现这些0~256号状态都被别人使用了,而且中国的汉字太多,于是中国人准备用两个字节来表示一个汉字,并应该兼容最初的0~127号字符集,于是中国人这样设计,当最高位为0时,计算机就按ASSCI码(1位)进行编码,当发现最高位是1时,就按两个字节进行编码,eg: 1001000'10001011 这两个字节都对应两个十进制数分别是72和11 ,于是就想设计一个二维矩阵建立与汉字的对应关系,按照这个二维矩阵的行和列一样进行查找对比,就可以确定一个汉字了,并且保留了0~127号码的对应规则(这就是我们经常发现为什么英文不乱码而中文乱码的原因).

​ 这个二维矩阵对应的字符集,就成为GB2312字符集,这样和后面的EASCCI就冲突了,然而日本、韩国等世界上几乎每个国家都有自己的字符集,于是后面的第128~256号字符集经常发生冲突(即乱码,计算机不知道应该按照GB2312、EASSIC、日文的字符集、等进行编码,所有就会出现乱码 )。

​ 这里补充说明一点,GB2312字符集表示的汉字也比较少,于是在GB2312的基础上扩展产生GBK编码以及GB 18030 等汉字字符集)

ANSI

​ 严格说ANSI并不是一个字符集。前面我们讲到每个国家都有自己的编码规则,于是Windows公司设计了ANSI规则,ANSI根据电脑操作系统的设置,自动关联对应的编码,eg: 在美国就按ASCII码进行编码,并自动设置为默认编码,同理在中国,就按GBK进行编码,而在日本,就按日本对应的字符集进行编码……

​ 所以ANSI简单来说是一个自动判别系统,但这样在不同字符进行切换时,就会出现乱码的问题。于是迫切希望有同一的编码规则,于是Unicode 编码孕育而生。

Unicode 、utf-8、utf-16、utf-32

​ 由于世界上存在多种编码,当不同国家的人在进行发送文件时,会出现乱码(因为解读的规则不一样,同一段0 1 代码,中国按照GBK去解读,而西欧安装EASSCI字符集去解读,这样必然计算机弄不清楚,必会出现乱码),于是有一个社区就说要不我们把世界上的所有文字都进行统一编码吧!这样就不会产生乱码了,于是产生了Unicode编码,当然Unicode是一个很大的集合,大概用了4个字节(即32位 0 1代码去表示),这样美国人或者西欧人不干了,因为原本我存储一个英文单词words只需要5个字节,现在用Unicode就变成了20个字节,这样硬盘的空间严重浪费,于是产生出来utf-8、utf-16、utf-32字符集,这里重点解释一下utf-8字符集,这个是可变长编码,计算机可以根据前面的一个1个字节的编码规则进行自动判断,该是用一个字节去解读还是用2个、3个字节去解读(这里这个规则有点复杂,反正计算机能从第一个字节中解读出来,到底应该用几个字节取解读)。

  • utf-8 : 使用1~4个字节存储一个Unicode字符。
  • utf-16: 使用2或4个字节存储
  • utf-32 : 使用4个字节存储

乱码产生的原因

​ 假设在一个文本编辑器中写入hollo world 你好世界,编码方式为utf-8

​ 当按下ctrl + s时,文本编辑器将会按照unicode字符集中查找每个字符对应的码,再按照utf-8的方式,将hollo world存为单字节,而将你好世界存成2~4个字节,此过程称为 编码

​ 关掉文件,以utf-8方式打开文件,此时编辑器读取所有字节,按照之前存入的逆向逻辑,将字节分成一段一段,每一段即表示一个字符,查unicode表,依次解读为hollo world 你好世界,此逆向过程称为 解码

​ 但假如上面的文件以gbk方式去解码会发生什么?gbk会按照它的逻辑,将每两个字节都当成一个字符,继而去gbk字符集里边去查对应的字符,结果可想而知,都是一些你不想看到奇奇怪怪的字符,不是你想要的字符,此即为乱码

​ 还有当在编程的时候,控制台显示器,能否支持该字符集,比如win下的cmd 默认支持GBk ,当你用UTF-8的字符去展示时,也会乱码,同样的道理,这是因为显示器的解读规则出了错误

常见的很多字符集有不同的名字

由于不同字符集 之间差异很小,或者是别名,于是都统称为一类字符,后面遇到在补充.

  • ANSI = Windows 1252 = CP 1252 = Windows code page 1252 = Windows Latin-1

常见的例子:

1、c 语言

c语言有时在编译的时候经常为出现乱码 ,这是怎么解决?

  • 首先,我们应该知道这个文件的编码方式 —- 可以用Notepad++ 去查看文件的编码,或者用记事本进行查看.

  • 其次,我们应该以对应编码的方式去解码(在c语言中进行编译.)这样就不会产生乱码了.

eg: 这里我准备在R中进行调用shell 命名去调用cmd命令来编译C语言.





#查看 当前cmd窗口的显示编码 一般为 936 代码GBK,
shell("chcp",intern = TRUE) # 参数intern = TRUE 把输出对象变成R对象(即输出在R中显示,并可以赋值给变量)


#########  如果文件是按照 UTF-8 编码的
shell("g++ -fexec-charset=GBK -finput-charset=UTF-8 testutf8.cpp -o abc.exe")
shell("abc.exe",intern = TRUE)

########### 如果文件是按照GBK编码的
shell("g++  testgbk.cpp -o abc1.exe && abc1.exe",intern = TRUE)

shell("rm abc1.exe")
shell("rm abc.exe")



########## 如果你不清楚你的文件编码,R语言也可以进行相关的判断(不是很准确)
# Ruchardet包 和 wand包

参考:

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

https://github.com/LeoYuan/leoyuan.github.io/issues/25

https://www.crifan.com/files/doc/docbook/char_encoding/release/webhelp/why_ansi_called_native.html


次;