编码问题

Posted 2018-05-9-ygk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编码问题相关的知识,希望对你有一定的参考价值。

字符编码常识以及问题解析

基本知常识
1、位和字节

位(bit)是计算机里存放的二进制值(0/1),而8个位组成的“位串”称为一个字节。
容易算出,8个位的组合有256(2的8次方)个组合方式,其取值范围是“00000000-11111111”,常用十六进制来表示。比

如“01000001”就是一个字节,其对应的十六进制值为“0X41”。
而我们通常所讲的字符编码,就是指定义一套规则,将真实世界里的字母/字符与计算机的二进制序列进行相互转化。

01000001(0x41) -->65 ->‘A‘
2、拉丁字符

拉丁字符是当今世界使用最广泛的符号了。通常我们说的拉丁字母,指的是基础的拉丁字母即指常见的“ABCD”等26个英文字母,

这些字母和英语中一些常见的符号(如数字,标点符号)称为基础拉丁字符,这些基础拉丁字符在使用英语的国家广为流传,当然

在中国,也被用来当做汉语拼音使用。在欧洲其他一些非英语国家,为满足其语言需要,在基础拉丁字符的基础上,加上了一些连

字符,变音字符“‘A‘‘”,形成了派生拉丁字母,其表示的字符范围在各种语言有所不同,而完整意义上的拉丁字符是指这些变体

字符与基础拉丁字符的全集。是比基础拉丁字符集大很多的一个集合。

编码标准

字符编码就是一套规则。既然是规则,就必须有一个标准。我们下来仔细说说常见的字符编码标准。
1、拉丁编码

ASCII的全称是American Standard Code for Information Inerchange(美国信息交换标准代码)。顾名思义,这是现代计算机的

发明国美国人设计的一套标准,而美国是一个英语国家,他们设计的ASCII编码也只是支持基础拉丁字符。ASCII的设计也很简单,

用一个字节(8个位)来表示一个字符,并保证最高位的取值一直为0。即表示字符含义的有效位数是7位,我们不难算出其可以表达

字符数为2的7次方=128个。这128个字符包括95个可打印字符(涵盖了26个英文字母的大小写以及英文标点符号)和33个控制字符(

不可打印字符)。

我们知道,ASCII是美国人设计的,只能支持基础拉丁字符,而当计算机发展到欧洲,欧洲其它国家不只是用的基础拉丁字符(即用

更大的派生拉丁字符集),那这样的话,该怎么办呢?

当然,最简单的办法是将美国人没有用到的第8位也用上就好了,这样能表达的字符个数就达到了2的8次方个=256,相比较原来,增

长了一倍,这个编码规则也常被称为EASCII。EASCII基本解决了整个西欧国家的字符编码问题。但是对于欧洲其它地方如北欧,东

欧地区,256个字符还是不够用,于是出现了ISO 8859,为了解决256个字符不够用的问题,ISO 8859采取的不再是单个独立的编码

规则,而是由一系列的字符集(共15个)所组成,分别称为ISO 8859-n(n=1,2,3...11,13,..15,16,注意没有12)。其每个字符集

对应不同的语言,如ISO 8859-1对应西欧语言,ISO 8859-2对应中欧语言等。其中大家所熟悉的Latin-1就是ISO 8859-1的别名,它

表示整个西欧的字符集范围。需要注意的一点事,ISO 8859-你和ASCII是兼容的,即其00000000(0x00)----01111111(0x7f)范

围段与ASCII保持一致,而10000000(0x80)-11111111(0xff)范围段被扩展道不同的字符集。

3、中文编码

以上我们接触到的拉丁编码,都是单字节编码,即用一个字节来表示一个字符。
但这个规则对于其它字符集更大的语言来说,并不

适用,比如中文,于是出现了用多个字节表示一个字符的编码规则。常见的中文GB2312(国家简体中文字符集)就是用两个字节来

表示一个汉字(注意一个汉字,对于拉丁字母,GB2312还是用一个字节来表示以箭筒ASCII)。我们用下表来说明各个中文编码之间

的规则和兼容性。

GB2312                       big5               gbk                                      GB18030

作用 国家简体中文字符集 统一繁体字符集 GB2312的扩展,加入对繁体字的支持 中日韩文字的编码

字节数 变字节: 2个字节 2个字节 变字节:
1个字节--兼容ASCII 1个字节--兼容ASCII
2个字节-汉字 2个字节,4个字节

范围 能表示74445个符号, 能表示21886个符号 能表示21886个符号 能表示27484个符号
包括6763个汉字

兼容性 兼容ASCII 兼容ASCII,
00-7f范围内是一位,
和ASCII保持一致 但和GB2312有冲突 兼容GB2312 兼容GB2312

对于中文编码,其规则实现上是很简单的,一般都是简单的字符查表即可,重要的是要注意其相互之间的兼容性问题。
如果选择big5字符集编码,就不能很好的兼容GB2312,当做繁体转化为简体时有可能导致个别字的冲突和不一致,但是gbk和GB2312

之间不存在这样的问题。

3、Unicode
以上可以看到,针对不同语言采用不同的编码,有可能导致冲突和不兼容性的问题,如果我们打开一份字节序文件,如果不知道其

编码规则,就无法正确解析其语义,这也是产生乱码的根本原因。有没有一种规则是全世界字符统一的呢?当然有,Unicode就是一

种。为了能独立表示世界上所有的字符,Unicode采用4个字节表示一个字符,这样理论上Unicode能表示的字符数就达到了2的31次

方=2147483648=21亿左右各字符,完全可以涵盖世界上一切语言所用的符号,我们可以以“微信”两个字举例说明:

微 <-> u5fae <-> 00000000 00000000 01011111 10101110
信 <-> u4fe1 <-> 00000000 00000000 01001111 11100001

容易从上面的例子里看出,Unicode对所有的字符编码均需要四个字符,而这对于拉丁字母或者汉字来说是浪费的,其前面三个或者

两个字节均是0,这对于信息存储来说是极大的浪费。另外一个问题就是,如何区分Unicode和其他编码也是一个问题,比如计算机

怎么知道四个字节表示一个Unicode中的字符,还是分别表示四个ASCII的字符呢?

针对以上两个问题,困扰了Unicode,让Unicode的推广上一直面临着困难。直至utf-8作为Unicode的一种实现后,部分问题得到解

决,才得以完成推广使用。utf-8是Unicode的一种实现方式,而Unicode是一个统一标准规则,Unicode的实现除了utf-8还有其它的

,比如utf-16等。

关于utf-8的基本规则,其实简单来说就两条:
①、对于单字节字符,字节的第一位为0,后7位位这个符号的Unicode码,所以对于拉丁字母,utf-8和ASCII是一致的。
②、对于n个字节的字符,第一个字节前面n位都设为1,第n+1位为0,后面字节的前两位一律为10,剩下没有提及的位,全部为这个

符号的Unicode编码。

通过以上规则,可以建立一个Unicode取值范围和utf-8字节表示的对应关系,比如下表:

Unicode符号范围(十六进制) utf-8编码方式(二进制)

单字节:0000 0000-0000 007f 0xxxxxxx
双字节:0000 0080-0000 07ff 110xxxxx 10xxxxxx
三字节:0000 0800-0000 ffff 1110xxxx 10xxxxxx 10xxxxxx
四字节:0001 0000-0010 ffff 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

举例来说,“微”的Unicode是‘u5fae‘,二进制表示是“00000000 00000000 01011111 10101110”,其取值就位于‘0000 0800-

0000 ffff‘之间,所以其utf-8编码为‘11100101 10111110 10101110‘(加粗部分为固定编码内容)

通过以上简单规则,utf-8采取变字节的方式,解决了我们前文提到的关于Unicode的两大问题。同时,作为中文使用者需要注意的

一点事Unicode(utf-8)与gbk,GB2312这些汉字编码规则是完全不兼容的,也就是这两者之间不能通过任何算法来进行转换,如需

转换,一般通过gbk查表的方式来进行。

常见问题及解答

1、Windows notepad中的编码ANSI保存选项,代表什么含义?

ANSI是Windows的默认的编码方式,对于英文文件时ASCII编码,对于简体中文文件时GB2312编码(只针对Windows简体中文版,如果

是繁体中文版会采用big5码)。所以,如果将一个utf-8编码的问题,另存为ANSI的方式,对于中文部分会产生乱码。

2、什么是utf-8的bom?

bom的全称是Byte Order Mark,BOM是微软给utf-8编码加上的,用于标识文件使用的是utf-8编码,即在utf-8编码的文件起始位置

,加入三个字符“EE BB BF”,这时候微软特有的,标准并不推荐包含bom的方式。采用bom的utf-8编码文件,对于一些只支持标准

utf-8编码的环境,可能导致问题,比如:在go语言编程中,对于包含薄膜的代码文件,会导致编译出错。

3、为什么数据库Latin1字符集(单字节)可以存储中文呢?

其实不管需要使用几个字节来表示一个字符,单最小的存储单位都是字节,所以,只要能保证传输和存储的字节顺序不会乱即可。
作为数据库,只是作为存储的使用的话,只要能保证存储的顺序和写入顺序一致,然后再按照相同的字节顺序读出即可,翻译成语

义字符的任务交给应用程序。比如“微”的utf-8编码是“0xE5 0xBE 0xAE”,那数据库也存储“0xE5 0xBE 0xAE”三个字节,
其它应用按顺序从数据库读取的话,在按照utf-8编码进行展现。这当然是一个看似完美的方案,但是之哟啊写入,存储,读取过程

中出现任何别的编码,都可能导致乱码。

4、mysql数据库中多个字符集变量,它们之间分贝是什么关系?

show variables like ‘character_set_&‘;

variable_name value

character_set_client utf-8
character_set_connection utf-8
character_set_database latin1
character_set_filesystem binary
character_set_results utf-8
character_set_server latin1
character_set_system utf-8
character_sets_dir /usr/share/mysql/charsets/

我么来解释下:

character_set_client:客户端来源的数据使用的字符集,用户客户端显示告诉服务器端所发送语句中的字符编码。

character_set_connection:连接层的字符编码,MySQL一般用character_set_connection将客户端的字符转化为连接层的字符。
character_set_results:查询结果从数据库读出后,将转换为character_set_results返回给前端。

而我们常见的解决乱码问题的操作时:
mysql_query("set names utf-8");

其相当于将以上三个字符集统一全部设置为utf-8,这三者是一致时,一般就解决了乱码的问题。

character_set_database;当前选中数据库的默认字符集,比如当create table时没有指定字符集,将默认选择该字符集。

character_set_database和character_set_system,一般用于数据库系统内部的一些字符编码,处理数据乱码问题时,我们基本可

以忽略。

5、什么情况下,表示信息丢失?

select hex(字段) from t;

通过查看存储的字节序,我们可以从根本上了解存储的内容是什么编码了。而当发现存储的内容全部是’3F’时,就表明存储的内

容由于编码问题,信息已经丢失了,无法再找回。































以上是关于编码问题的主要内容,如果未能解决你的问题,请参考以下文章

以下代码片段 C++ 的说明

通过 findById 访问活动布局中的硬编码片段

*** Bool 编码为数字属性列表片段。属性列表编码器

《安富莱嵌入式周报》第279期:强劲的代码片段搜索工具,卡内基梅隆大学安全可靠C编码标准,Nordic发布双频WiFi6 nRF7002芯片

如何测试文本片段是不是是 Quoted-printable 编码的

Sublime Text自定制代码片段(Code Snippets)