文章内容只是个人认识,用于整理和日后回忆。
编码
计算机出现之后,输入问题就一直随着计算机的普及和传播而扩展。从一开始的ascii就可以支持全部内容,到各国语言的输入,然后是统一规范的出现。
基本概念
其实关于字符集和编码方式,总是很容易叫混,查资料时经常遇到教的不一样的。我选择了一种自我感觉比较合理的叫法。?
字节: 字节是计算机存储数据的基本单位。都知道数据的表示是用位(bit)。1字节=8bits
字符: 针对不同编码方式有不同的大小,通常都是n个字节。
字节序: 在字符存储到计算机中时,对于超过一个字节的字符的字节排序方式。
BOM: Byte Order Mark,字节序表示,utf-16和utf-32前必须表明,utf-8正规是没有的,但是也给了一种格式,就是带BOM的utf-8,就是在文件内容最前面加上BOM。
字符集: 人类识别的字符与定义的数字对应的集合。比如:ANSI、Unicode
代码页: 表示不同的ANSI集合。与Unicode只维护一个集合不同,ANSI集合众多,每个集合都是一个代码页。可以在Windows中选择不同代码页来选择你使用的语言。
编码方式: 字符集的实现。不同实现采用不同的数据结构来存储对应的数字。相同字符集的编码方式,针对同一字符时,存储的数字是一致的。比如:gb2312
、utf-8
。
字节序–大端小端
据说大端小端的起源是《格列佛游记》:Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端(big endian),可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端(little endian),违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。
虽然故事中的大端小端和机器中的大端小端并没有生么相似性,但是还是采用了使用了他们的名字,而且使用至今?。如果不能处理好,不同系统之间的字节序问题,也会造成故事中那样严重的后果。交互的数据会一塌糊涂。
区别
字节序就是数据存储的顺序。并不是所有的数据都是8bit,比如c语言中的int、long等数据类型。所以,对于超过了8bit的数据,有着存储方式的差异。即低地址存储数据高位还是地位,也就是大端小端的区别。
举一个例子,一个32bit的数据:0x1234
。12
就是数据的高位,34
就是数据的地位。存储地址假设从0x1000
开始。
地址 | 大端数据 | 小端数据 | |
---|---|---|---|
低位 | 0x1000 | 12 | 34 |
高位 | 0x1001 | 34 | 12 |
特点
大端模式:符号位固定在第一个字节,容易判断正负。
小段模式:强制转换不需要调整字节内容。
常见的字节序
cpu的字节序
- cpu的架构
- x86
- PowerPC
- ARM
cpu的架构其实就是规定了cpu操作的指令集。其中以x86为势头最猛,他是有Intel推出的兼容8bit、16bit以及32bit的cpu架构。但是现在,随着cpu的发展,32bit已经远远满足不了需求,相应的发展已经不完全受Intel控制。x86架构后的64bit,也面临着需要兼容低版本等等问题。这些不在这里讨论。
每个品牌的计算机使用不同的架构。首先是架构的推出:
架构 研发公司公司 x86 Intel PowerPC IBM、Apple和Motorola ARM ARM 然后是部分主流产品的使用:
使用公司 架构 Intel x86 AMD x86 苹果 前期PowerPC,05年以后x86 ARM ARM IBM PowerPC
- 架构对应的字节序:
架构 | 字节序 |
---|---|
x86 | 小端模式 |
PowerPC | 打断模式 |
ARM | 硬件选择,两种都可以,默认是小端 |
语言的字节序
语言 | 字节序 |
---|---|
c | 跟随系统 |
Java | 大端模式 |
这里需要注意,Java的运行相当于是自己开了一个虚拟机,所以可以做到全平台运行。他包装了底层cpu的指令,采用大端模式。
这里就可以回顾一下开头的大小端问题。如果有两个系统,一个是c语言程序,一个是Java语言程序,而c又是在Intel或者AMD的cpu上(显然大部分情况都是这样的)。如果直接交换数据,那么数据就跑偏了。
网络协议的字节序
为了统一,默认就是大端模式。
字符集
我所知道的字符集也就是ANSI和Unicode这两种了。还有查资料时遇到的EBCDIC,过于古老,丢弃。
ANSI与Unicode的分割是很明显的。ANSI有着众多不同定义,基本每种语言都有不下一种,为此还诞生了代码页这一特殊概念。Unicode则是以强势之姿包揽了所有字符,全部放在了一个集合里。
ASCII
ASCII码可以说是计算机的起源编码,沿用至今,而且还被几乎所有的编码方式兼容,地位不可谓不高。所以单独拎出以示尊敬。
ASCII码以128个代码位表示了起初电脑的所有符号和操作位。其中前33个和最后1个都是控制符(0-32)、后面的则表示各种可见符号。在早期,计算机只在美国等国家传播时,ASCII码就足以支撑全部操作了。但是随后,计算机走向世界,需要各种语言的符合加入,ASCII就不太够用了。
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
ANSI
ANSI出现早于Unicode,而且没有美国因为个别原因的阻挠,使用效果应该是比Unicode家族的要风光的多。那个个别原因在Unicode在下一节吐槽。
ASCII码显然只用了一半,全部用在了首位为0的代码位上。后面还有空余的怎么版,还有这么多其他语言的符号没有加入,怎么可以浪费!
ANSI是在计算机传播时,针对8bit的剩余空间研发的各个语言的版本。当时没有统一组织出面制定规则,所以出现了百花齐放的情况。基本形成了一种语言一种集合,甚至是多种集合。当然,他们都是想办法利用127号之后的位置,没有改动前面的。而对于像汉语这种规模庞大的字符集,更是采取了双字节表示。 不过紧接着问题出现了,每种集合都占用了相同的位置,位置该归谁呢?
由此出现了代码页的概念。系统依据居住地自动给选择对应的代码页,采用相对应的字符集合。这里集合与编码方式是一一对应的。如gb2312即表示简体中文的所有集合,也代表着编码方式。
简单列举几个:
编码 | 应用 |
---|---|
iso-8859-1 | 部分欧洲语言 |
gb2312 | 简体中文 |
big-5 | 繁体中文 |
gbk | 简体中文和繁体中文 |
gb18030 | 少数民族文字、简体中文和繁体中文 |
utf-8 | 世界各国的语言 |
Unicode
Unicode的出现可以说是必然的,因为总不能每次国际交易都要来回的转换编码方式,那也太麻烦了。于是Unicode出现了,他收集了地球上所有语言符号,以及一些其他符号。而且让然选择了将ASCII码的地位放在最前面。因为符号之多,采用了两个字节才能完全表示,这也是Unicode的第一个版本ucs-2。ucs-2的概念显然比较通俗易懂,就是说用两个字节表示符号。
平静一段时间后,美国不满足了,他们的字符全部在前面,所以使用时每个字符都固定有一个字节全为0,他们不爽,就不能直接使用Unicode作为编码方式。原本字符集与编码方式统一的格局也被迫改变了,Unicode只作为字符集,而不再表示编码方式。也因此诞生了utf-8。
Unicode目前有两个主要版本:ucs-2、ucs-4。ucs-2使用2个字节,ucs-4使用4个字节、而且ucs-4也是沿用ucs-2的代码位,外加了一些平面辅助的符号。ucs-4只用到了21个字符,不过考虑到以后的发展和方便还是使用了4个字节。目前来说,ucs-4一般还是用不到的。
Unicode目前有4种使用编码方式,如:utf-8、utf-16和utf-32。由于历史遗留问题,Unicode的编码方式也被放入了代码页中,不过显然,这个种类就少了很多。
utf-7严格来说并非unicode认定的实现方式之一。只是曾为实现邮件发送而提出将unicode转换为US-ASCII的一种方式。曾经的邮件发送并不支持非ASCII码的传输。
Unicode的出现所示真正的解决了国际化问题,目前主流的计算机中的cpu处理数据时,包括Java模拟的虚拟机,都是将硬盘中的数据翻译成Unicode再加载到内存中的。保存时,再翻译成对应的数据放回。
编码方式
编码方式的种类实在是太多了,只讲几个常用的。前面讲过的ASCII码就不再罗嗦了。
中文
中文的编码方式主要有四种。最出版的gb2312,由中国国家标准总局1980年房补,只包含简体中文;然后是big-5,由台湾财团法人信息产业策进会为五大中文套装软件(并因此得名Big-5)所设计的中文共通内码,在1983年12月完成公告,用于繁体字;GB13000在Unicode1.1发布同年发布,但是一直未被业界采用;gbk,微软利用了GB2312中未使用的编码空间,并且收录了GB13000中的全部字符,从而定制了GBK编码(虽然收录了GB13000的全部字符,但是编码方式并不相同);gb18030,也是中国正式发布的,包含了部分少数民族语言,简体中文、繁体中文,还有日韩所有文字。
中文的编码都是至少两个字节的,字节序都是采用大端模式。这些编码方式其实都不涉及字节序,不想utf-16还分成utf-16BE和utf-16LE两种编码方式。
gb2312
比较重要的中文编码方式,中内地比较常用。表示英文一个字节,表示中文两个字节。
gbk
也是比较常用的编码方式,基于gb2312扩展,完美兼容。表示英文一个字节,表示中文两个字节。
gb18030
该编码方式不定。分为1、2和4字节三种。
通用
通用的编码格式不多,各有好处。不过个人觉得utf-16LE应该是最理想的,但是使用最多的必然还是utf-8。
utf-8
UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。UTF-8转换表表示如下:
Unicode/UCS-4 | bit数 | UTF-8 | byte数 | 备注 |
---|---|---|---|---|
0000 ~007F | 0~7 | 0XXX XXXX | 1 | |
0080 ~07FF | 8~11 | 110X XXXX10XX XXXX | 2 | |
0800 ~FFFF | 12~16 | 1110XXXX10XX XXXX10XX XXXX | 3 | 基本定义范围:0~FFFF |
1 0000 ~1F FFFF | 17~21 | 1111 0XXX10XX XXXX10XX XXXX10XX XXXX | 4 | Unicode6.1定义范围:0~10 FFFF |
20 0000 ~3FF FFFF | 22~26 | 1111 10XX10XX XXXX10XX XXXX10XX XXXX10XX XXXX | 5 | 说明:此非unicode编码范围,属于UCS-4 编码早期的规范UTF-8可以到达6字节序列,可以覆盖到31位元(通用字符集原来的极限)。尽管如此,2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。根据规范,这些字节值将无法出现在合法 UTF-8序列中 |
400 0000 ~7FFF FFFF | 27~31 | 1111 110X10XX XXXX10XX XXXX10XX XXXX10XX XXXX10XX XXXX | 6 |
utf-8其实本身是只有一种格式的,而且是大端模式。但是由于utf-16和utf-32有两种,莫名的给utf-8也加了一种。叫做带BOM的utf-8,字节序还是大端模式,但是在文件开头加上了FE FF
,就是大端模式的标记。
utf-16
分为两种:
- utf-16LE
- utf-16BE
utf-16LE
采用小端模式,文件开头有FF FE
的标识。
utf16-BE
采用大端模式,文件开头有FE FF
的标识。这个其实可以理解为Unicode的ucs-2完全复制。编码格式与符号位完全对应。
utf-32
基本与utf-16一样,不过对应的是ucs-4。