字符的编码(四)

(七)Unicode编码方案概述

前面讲过,随着计算机发展到世界各地,于是各个国家和地区各自为政,搞出了很多既兼容ASCII但又互相不兼容的各种编码方案。这样一来同一个二进制编码就有可能被解释成不同的字符,导致不同的字符集在交换数据时带来极大的不便。

比如大陆和台湾是只相隔150海里、使用着同一种语言的兄弟地区,也分别采用了不同的DBCS双字节字符集编码方案。

以前大陆地区必须装上类似于“UCDOS希望汉字系统”这样的中文处理系统专门来处理简体汉字的显示、输入问题。

而台湾地区由于采用BIG5编码方案(统一繁体字编码,俗称大五码,使用2个字节表示繁体汉字),则必须安装类似于“ET倚天汉字系统”这样的繁体中文处理系统才可以正确显示、输入繁体汉字。

因此,要想打开一个文本文件,就必须首先知道它所采用的编码方案,否则用错误的编码方案进行解码,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方案不一样。

想象一下,如果有一种统一的编码方案,将世界上所有语言字符都纳入其中,每一个字符都给予一个全球独一无二的编码,那么乱码问题就会消失。于是,全球所有国家和民族使用的所有语言字符的统一编码方案诞生了。

最初,由多语言软件制造商组成了统一码联盟(The Unicode Consortium),然后于1991年发布了The Unicode Standard(统一码标准),定义了一个全球统一的通用字符集,习惯简称为Unicode字符集(Unicode常被称为统一码、万国码、单一码,严格来说,这种称呼不够严谨或不够准确,因为Unicode字符集只是一个编号字符集,尚未经过字符编码方式CEF和字符编码模式CES进行编码)。

接着,ISO及IEC也于1993年联合发布了称之为Universal Multiple-Octet Coded Character Set(通用多八位组编号字符集,习惯翻译为“通用多八位编码字符集”)、简称为UCS (Universal Character Set通用字符集)、标准号为ISO/IEC10646-1的全球统一的通用字符集。

后来,统一码联盟与ISO/IEC双方都意识到世界上没有必要存在两套全球统一的通用字符集,于是进行整合,并为创立一个单一的全球统一的通用字符集而协同工作。到Unicode 2.0时,Unicode字符集和UCS字符集(ISO/IEC 10646-1)基本保持了一致。

虽然现在两个项目仍都存在,并独立地公布各自的标准,但统一码联盟和ISO/IEC都同意保持两者的通用字符集相互兼容,并共同调整未来的任何扩展。

目前,Unicode的知名度要比UCS知名度大得多,已成了全球统一的通用字符集或编码方案的代名词。并且在实践中,Unicode也要比UCS应用得更为广泛得多。

Unicode字符集的目标是涵盖目前人类使用的所有字符,并为每个字符分配唯一的字符编号(即码点编号、码点值),一一对应于编号空间(Code Space代码空间、码空间、码点空间)里的码点(Code Point代码点)。

目前,Unicode字符集将所有字符按照使用上的频繁度划分为17个平面(Plane层面),每个平面上的编号空间有2^16=65536个码点。将来根据需要,还可扩展为更多平面。

其中第0个平面BMP(Basic Multilingual Plane基本多语言平面、基本多文种平面、基本平面、平面0),基本涵盖了当今世界上正在使用中的常用字符。我们平常用到的Unicode字符,一般都是位于BMP平面上的。

BMP平面以外其他的增补平面(也称为辅助平面、SP平面)要么用来表示一些非常特殊的字符(比如不常用的象形文字、远古时期的文字等),且多半只有专家在历史和科学领域里才会用到它们;要么被留作扩展之用。目前Unicode字符集的17个平面中尚有大量编号空间未被使用。

另外,BMP平面有一个专用区(Private Use Zone):0xE0000xF8FF(十进制5734463743),共6400个码点,被保留为专用(私用),因而永远不会被分配给任何字符;还有一个被称为代理区(Surrogate Zone)的特殊区域:0xD800-0xDFFF(十进制55296~57343),共2048个码点,目的是用基本平面BMP中的两个码点“代理”表示BMP以外的其他增补平面SP的字符(解释详见后文)。

字符的编码(四)_第1张图片
image.png

Unicode字符集的字符编码方式一开始规定用两个字节的码元(即16位码元)来统一表示所有的字符(即UTF-16编码方式,UTF-16编码方式要早于UTF-8编码方式、UTF-32编码方式出现,详见后文)。

对于ASCII字符,与前面介绍的ANSI编码一样,Unicode也保持其原编码不变(准确地说,应该是保持其“编号不变”,因为在传统字符编码模型中,编号与编码不作区分,说“编码不变”也勉强可以),只是在UTF-16字符编码方式中将其长度由原来的8位扩展为16位(注意,这里的字符编码方式CEF还只是逻辑意义上的码元序列,不是字符编码模式CES——物理意义上的字节序列),而其他文化和语言的字符则全部重新统一编码。

由于ASCII字符只需要用到UTF-16的16位编码中的低8位,所以其高8位永远是0(实际上也只用到了低8位中的低7位,因此准确地说其高9位永远是0)。

在Unicode标准最初推出的UTF-16字符编码方式中,无论是半角的英文字母,还是全角的汉字,它们都表示统一的“一个字符”,同时其编码也都是统一的“两个字节”(也因此UTF-16属于双字节码元编码方式,而Unicode标准在UTF-16字符编码方式之后所推出的UTF-8字符编码方式则属于单字节码元编码方式,两者之间的关系与区别详见后文)。

请注意这里的“字符”和“字节”两个术语意义上的不同:“字节”是一个与计算机相关的物理意义上的8位存储单元,而“字符”则是一个与文化相关的逻辑意义上的文字符号。

在Unicode标准推出之前,那些做多语言国际软件的公司遇上过很大麻烦。他们为了在不同的国家销售同一套软件,就不得不特别注意字符编码的问题。不仅要处处小心不要搞错,还要把软件中的文字在不同的字符编码中转换来转换去,而Unicode标准的出现,提供了一个很好的一揽子解决方案。

于是从Windows NT开始,微软趁机把操作系统改了一遍,把所有的核心代码都改成了采用Unicode标准的版本(实际使用的就是Unicode标准的UTF-16字符编码方式CEF所对应的UTF-16字符编码模式CES)。

从Windows NT开始,Windows系统终于无需要加装各种本土语言系统(比如“UCDOS希望汉字系统”之类的),就可以直接显示全世界上所有的字符了。当然,为了保持兼容性,对于之前的ANSI编码方案,Windows仍然是必须支持的。

Unicode在刚开始制订UTF-16字符编码时,并没有考虑与任何一种现有的字符编码保持完全兼容(与ASCII也只能算是间接兼容或者说半兼容,毕竟ASCII字符的UTF-16编码也同样是16位的,前9位均为0),比如GBK与Unicode在汉字的编码上完全是不一样的,没有任何一种简单的算术方法可以将文本内容在UTF-16编码和GBK编码之间进行直接转换,要转换的话只能通过查表这样低效率的笨办法一个字符对应一个字符地来进行。

即便是ASCII字符,也属于不完全兼容,因为UTF-16也是用两个字节来表示的,虽然低7位与ASCII保持了一致,其余高位的9位均只是占位的0,但毕竟还是使用了16位共两个字节编码,不同于ASCII码的单字节编码。正是鉴于此(当然除此之外还有其他原因),于是后来又设计了UTF-8字符编码方式,则保持了跟ASCII码的完全兼容。

从字符集的角度上来讲,Unicode字符集不同于ASCII这样不能再增加字符的封闭字符集,而是一个开放的字符集,是可以不断增加字符的。因此Unicode字符集也在不断发展(比如随着互联网即时聊天工具的发展而流行起来的很多Emoji表情符就不断地被增加到了Unicode字符集中),理论上支持的字符数量是没有上限的,未来还可再扩展。

(注意,很多文章中,有时候称字符集,有时候称字符编码方案,大致上来讲,字符集与字符编码方案经常被视为同义词,尤其是在传统字符编码模型中。但若深究起来的话,在现代字符编码模型中,由于字符集实际上为编号字符集的简称,因此字符编码方案实际上涵盖了字符集。具体可参看前面对于现代字符编码模型的解释。)

字符的编码(四)_第2张图片
image.png

另外,与Unicode字符集基本保持兼容的ISO/IEC UCS字符集分为UCS-2(2-byte Universal Character Set)和UCS-4(4-byte Universal Character Set)两部分,分别以2字节和4字节表示字符编号(即码点编号)。

(注意,UCS-2和UCS-4只是表示字符编号的字节数不同,与字符编码方式CEF中的2字节与4字节没有关系,也因此不能分别等同于Unicode编码方案中的UTF-16和UTF-32字符编码方式CEF,有不少文章中称两者等同,这是完全错误的。事实上,由UCS-2和UCS-4所组成的UCS字符集跟Unicode字符集一样,都可分别采用UTF-8、UTF-16和UTF-32字符编码方式CEF对字符编号进行编码。)

其中,UCS-2又被称为基本多语言平面BMP(Basic Multilingual Plane),与Unicode的基本多语言平面BMP基本上保持了一致(但仍有部分不同,详见后文解释);而UCS-4格式用四个字节中的31位来表示一个字符的码点编号(即字符编号),这样可表示21亿个不同的字符(2^31=2147483648;最高位为0,另有用途)。

(注:很多文章中的UCS-4这个概念实际上有广义和狭义两种含义上的不同,广义UCS-4包含了UCS-2,狭义则不包含,需根据上下文作不同理解。)

不过,实践中UCS字符集应用得不多,基本以Unicode字符集为主,因此不作详细介绍。

Unicode字符集不仅给每个字符根据其所在的码点分配了一个唯一的码点值(即码点编号,不严格地来讲,也勉强可认为是字符编号,注意不要跟UTF-16、UTF-8等字符编号的编码方式混淆了概念),而且赋予了一个正式的名称:在表示一个Unicode码点编号(或UCS码点编号)的十六进制数的前面加上“U+”,称之为码点名称,或字符名称(考虑到有部分码点实际上并未分配字符,因此应称为“码点名称”更为合适,但很多文章中都习惯称为“字符名称”)。

比如,U+0041表示英语大写字母A,U+4E25表示汉字“严”。具体的字符对应表,可以查询unicode.org,汉字也可查询专门的中日韩汉字Unicode编码表

Unicode字符集中的U+0000U+007F(即十进制的0127)与ASCII字符集(即ISO/IEC 646标准)是一致的,U+0000U+00FF(即十进制的0255)与ISO/IEC 8859-1字符集(即Latin-1字符集)也是一致的。

(八)字符编码方案的演变与字节序

一、字符编码方案的演变

前文已经提及,编号字符集CCS(简称字符集)与字符编码方式CEF(简称编码方式)这两个概念,在早期并没有必要严格区分。

在Unicode编码方案出现之前,字符集及其具体的编码方式是绑定耦合在一起的,因此,“字符集”、“编码”或“编码方式”甚至“编码方案”这几个概念经常相互指代、彼此混用。

比如,字符集里的字符编号(即码点编号)在很多文章里也称之为字符编码、字符码、码点、码位、码点值、码值等,字符编码也称之为编码实现、编码方案、编码方式、编码格式、编码形式、内码、编码值、码值(你没看错,字符编号与字符编码都有可能被简称为“码值”,头大了吧),等等,非常混乱。

对于ASCII、GB2312、GBK、GB18030、Big5之类采用传统字符编码模型的历史遗留方案来说,由于基本上一个字符集只使用一种编码方式,因此这种混用问题还不大。

但在Unicode这样采用现代字符编码模型的全新方案出现之后,很多人受上述这些历史遗留方案的影响,从而导致无法正确地理解字符集和编码方式的关系,这导致了概念混乱,引起了大量误解。

然而,对于采用现代字符编码模型的Unicode标准来说,字符集和编码方式是必须明确区分的。从软件工程的角度来讲,传统字符编码模型中紧密绑定耦合在一起的字符集及编码方式这两个概念,在现代字符编码模型中被分离解耦了,而这种解耦带来了极大的灵活性。

这意味着,对于采用现代字符编码模型的同一个字符集,可以采用多个不同的编码方式对其字符编号进行编码。也因此,作为同一个Unicode字符集,目前就定义了UTF-8、UTF-16和UTF-32等(UTF,即Unicode/UCS Transformation Format)多种可选的编码方式。

所以,用Unicode来称呼一个编码方式已不合适,并且容易产生误导,引发混乱和导致困惑,而应该用UTF-8、UTF-16和UTF-32来称呼编码方式,以Unicode来称呼字符集,将包括Unicode字符集及各UTF编码方式等在内的整体称之为字符编码方案或字符编码系统或字符编码标准。

另外,同一字符编码方式CEF的码元序列,在计算机实际处理、存储和传输时,还需再次编码转换为字符编码模式CES的字节序列。

字符编码方式CEF的码元序列可理解为字符编码的逻辑表示形式,相对而言,字符编码模式CES的字节序列则可理解为字符编码在计算机中的物理表示形式。

而字节序列,则涉及到了不同的字节序(Byte-Order,主要分为大端序Big-Endian、小端序Little-Endian)。

二、字节序

字节序,又称字节顺序,其英文为Byte-Order;另外英文中还可称为Endianness,因此也翻译为端序。

Endianness这个词,源自于1726年Jonathan Swift的名著:Gulliver's Travels(格列佛游记)。在书中有一个童话故事,大意是指Lilliput小人国的国王下了一道指令,规定其人民在剥水煮蛋时必须从little-end(小端)开始剥。这个规定惹恼了一群觉得应该要从big-end(大端)开始剥的人。事情发展到后来演变成了一场战争。后来,支持小端的人被称为little-endian,反之则被称为big-endian(在英语中后缀“-ian”表示“xx人”之意)。


字符的编码(四)_第3张图片
image.png

1980年,Danny Cohen在他的论文“On Holy Wars and a Plea for Peace”中,第一次使用了Big-endian和Little-endian这两个术语,最终它们成为了异构计算机系统之间进行通讯、交换数据时所要考虑的极其重要的一个问题。

(注:所谓异构是指不同架构、不同结构、不同构造等,而这里的异构计算机系统,主要指的是采用不同CPU和/或不同操作系统的计算机系统。)

为什么会存在字节序的问题?

当然不会是像童话故事里那样出于“无厘头”的原因,而是因为历史上设计不同计算机系统的人在当时基于各自的理由和原因(这里的理由和原因网上存在着各种不同的说法,但也或许根本就没有具体理由和原因,只是设计人员的个人偏好,甚至是随意决定的),在各自计算机系统的设计上作出了不同的选择。

字节序共分为三种:

大端序BE(Big-Endian,也称高尾端序);
小端序LE(Little-Endian,也称为低尾端序);
中间序ME(Middle-Endian,也称为混合序),不太常用,本文不作介绍。

字节序,具体来说,就是多字节数据(大于一个字节的数据)在计算机中存储、读取时其各个字节的排列顺序。

字节序也被称为端序,这里的“端”,是指多字节数据中位于两端的字节,很多情况下还特指尾端字节(也称为小端字节)。

所谓尾端字节或小端字节,假设按照人对文字通常从左到右(或从上到下)的读写顺序来看的话,多字节数据位于右端(或下端)的低位字节就是尾端字节或小端字节,而将位于左端(或上端)的高位字节称为头端字节或大端字节。

当然,如果不按照通常从左到右的顺序,而是按照从右到左的顺序,那么多字节数据位于右端的高位字节就是头端字节或大端字节,而将位于左端的低位字节称为尾端字节或小端字节。

可见,不论读写顺序如何,所谓大端、头端,指的是多字节数据中,代表更大数值的那个字节所在的那一端,而相反的那一端则是小端、尾端。

而要彻底讲清楚大端序(高尾端序)、小端序(低尾端序),则需要从人读写二进制数的方向和内存地址的增长方向两者相结合讲起:

人读写二进制数的方向为(这是确定不变的):左--->右,大端/头端/高位--->小端/尾端/低位;或上--->下,大端/头端/高位--->小端/尾端/低位;

内存地址的增长方向则为(这是确定不变的):左--->右,低地址--->高地址;或上--->下,低地址--->高地址。

不过,计算机在内存中存取数据的方向则不是确定不变的,而是分为两种(注意,由于人的读写方向和内存地址增长方向是确定不变的,因此这里指的是计算机在内存中“书写”或“阅读”数据的方向):

1) 左--->右,大端/头端/高位--->小端/尾端/低位;或上--->下,大端/头端/高位--->小端/尾端/低位;

这种情况下,站在人的读写方向和内存地址增长方向(这两者的方向刚好一致)的角度来看,则是:大端在左(或在上),所以称之为大端序;或者说尾端在内存高地址,所以称之为高尾端序(即内存高地址存放多字节数据的尾端字节的字节顺序)。

2) 右--->左,大端/头端/高位--->小端/尾端/低位;或下--->上,大端/头端/高位--->小端/尾端/低位。

这种情况下,站在人的读写方向和内存地址增长方向(这两者的方向刚好一致)的角度来看,则是:小端在左(或在上),所以称之为小端序;或者说尾端在内存低地址,所以称之为低尾端序(即内存低地址存放多字节数据的尾端字节的字节顺序)。

【特别提示:大端序、小端序特别容易搞混,不好记忆;因此,建议使用高尾端序、低尾端序,本人是按下面这个方法来记忆的,很容易记住:存储的数据分头和尾——左头右尾,内存的地址分低和高——左低右高;因此,“高尾端”表示内存的高地址存储数据的尾端,“低尾端”表示内存的低地址存储数据的尾端。】

注意,这里的“数据”指的是数据类型意义上的数据,因此,准确地说字节序只跟多字节的整型数据类型有关,比如int、short、long型;跟单字节的整型数据类型byte无关。

那么,为什么就只跟多字节的整型数据有关,而跟单字节的整型数据无关呢?

因为在现代计算机中,字节是计算机数据存储的基本单位,对于整体上的单一字节(a byte),涉及到的是其8个比特位的顺序(位序、比特序,由于一般直接由硬件处理,程序员大致了解即可,这里不深入探讨),显然不存在字节序问题。

然而,对于在数据类型上作为一个整体的多字节数据而言,它是由各个可被单独寻址的字节所组成的(处理器寻址的最小单位一般是1个字节),由于历史的原因,其各个字节的存储顺序在不同的系统平台(包括CPU和操作系统)上是不同的。

也就是说,如果计算机处理的数据是单字节数据类型(byte),是不存在字节序问题的,因为单个字节已经是处理器寻址的最小单位以及存储和传输的最小单元了,存储时单字节数据类型直接进行,读取时也不存在根据前后2个字节才能解析出其值的情况,而构造字节流时也不会从一个单字节数据类型的值当中产生2个或以上的字节(既然是单字节数据类型,构造字节流时当然只可能产生1个字节)。

但是,如果计算机处理的数据是多字节数据类型(int、short、long等),虽然由于构成它们的2个或2个以上的字节是作为一个整体来进行处理的(比如以汇编语言中的数据类型word或dword为单位进行一次性处理,而不是以byte为单位分次处理;更深入地来讲,CPU一般是以字为一个整体来处理数据的,当单个数据不足一个字长时,则将多个数据“拼成”一个字再进行处理),但问题是字节才是CPU对内存寻址的最小单位以及存储和传输的最小单元。

因此,CPU在读取作为一个整体来进行处理的多字节数据类型的数据时,必须根据前后2个或2个以上的字节来解析出一个多字节数据类型的值;而且构造字节序列时也会从一个多字节数据类型的值当中产生2个或2个以上的字节。

这样一来,多字节数据类型的数据内部各字节间的排列顺序,是会影响从字节序列恢复到数值的;反之,也会影响从数值到字节序列的构造。

所以,在存储和读取多字节数据类型的数据时,必须按照计算机系统所规定的字节序进行(这一点程序员了解即可,计算机会自动处理);而尤其是在跨字节序不同的异构计算机系统进行通讯并交换数据时,通讯的任何一方更是必须明确对方所采用的字节序,然后双方将各自接收到的数据按各自的字节序对数据进行转换(有时候需要程序员专门编写转换程序),否则通讯将会出错,甚至直接失败。

实际上,int、short、long等数据类型一般是高级编程语言层面的概念,更进一步而言,这其实涉及到了机器硬件层面(即汇编语言)中的数据类型byte字节、word字、dword双字等在硬件中的表达与处理机制(实质上字节序跟CPU寄存器的位数、存放顺序密切相关)。具体可参看附文:《本质啊本质之一:数据类型的本质》、《寄存器与字、字节》。

【附:本质啊本质之一:数据类型的本质

CSDN博客 博主:band_of_brothers 发表于:2007-10-10 22:20

研究一个层面的问题,往往要从更深的层面找寻答案。这就如C语言与汇编、汇编与机器指令,然而终究要有个底限,这个底限以能使我们心安理得为准,就好比公理之于数学、三大定律之于宏观物理。

在这里就将机器指令作为最后的底限吧,尽管再深入下去还有微指令,但那毕竟是太机器了,可以了。以下所有从C代码编译生成汇编代码用的是命令:cl xxx..c /Fa /Ze。

类型的本质

类型这个概念,好多地方都有讲,但说实话,你真的理解吗?什么是类型?类型是一个抽象的概念还是一个真实的存在?嗯?

开始:

1)“好多相同或相似事物的综合”(辞海)。

2)X86机器的数据类型有byte、word、dword、fword、tword、qword,等等。

3)“给内存块一个明确的名字,就象邮件上的收件人一样。给其一个明确的数据类型,就好象说,邮件是一封信,还是一个包裹。”

4)类型就是一次可以操作的块的大小,就是一个单位,就像克、千克、吨一样。双字一次操作32位;字,一次操作16位;如果没有各种类型,机器只有一个类型单位——字节,那么当需要一个4字节大小的块时,就需要4次操作,而如果有双字这个类型单位,那么只需要一次操作就可以了。

5)类型,是机器层面支持的,不是软的,是硬的,有实实在在的机器码为证。


字符的编码(四)_第4张图片
image.png

附:寄存器与字、字节

字节:记为byte,一个字节由8个比特(bit)组成,可以直接存在一个8位寄存器里

1 0 1 0 1 0 0 1

一个字节

字:记为word,一个字由2个字节(共16比特)组成,可以直接存在一个16位寄存器里

1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0

高位字节 低位字节

一个8位寄存器用2位十六进制数表示,只能存1个字节(1个byte类型的数据)

一个16位寄存器用4位十六进制数表示,可存1个字(1个word类型的数据)或2个字节(2个byte类型的数据)

一个32位寄存器用8位十六进制数表示,可存2个字(1个dword类型的数据或2个word类型的数据)或4个字节(4个byte类型的数据)】

数据类型介绍到此为止

下面简要介绍一下字节序的三种类型:

字符的编码(四)_第5张图片
image.png

常用的操作系统中,linux和windows是小端法来存储数据对象的。

字符的编码(四)_第6张图片
image.png

另外,还一种网络字节序(network byte order网络字节顺序、网络序)。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。IP协议中定义大端序Big Endian为网络字节序。

不过,容易令人困惑的是,IP协议作为网络层协议,其面向的数据是报文,是没有字节的概念的,也就无关字节序了。因此,英文版wikipedia上说:

In fact, the Internet Protocol defines a standard big-endian network byte order. This byte order is used for all numeric values in the packet headers and by many higher level protocols and file formats that are designed for use over IP.

也就是说,IP协议里的字节序实际上是用在分组头里的数值上的,例如每个分组头会包含源IP地址和目标IP地址,在寻址和路由的时候是需要用到这些值的。

比如,4个字节的32 bit值以下面的次序传输:首先是高位的0~7bit,其次8~15bit,然后16~23bit,最后是低位的24~31bit。这种传输次序称作大端字节序。由于TCP/IP头部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。

再比如,以太网头部中2字节的“以太网帧类型”字段,表示是的后面所跟数据帧的类型。对于ARP请求或应答的以太网帧类型来说,在网络传输时,发送的顺序是以大端方式进行的:0x08,0x06。其在内存中的映象如下所示:
内存低地址


0x08 -- 高位字节

0x06 -- 低位字节


内存高地址

该字段的值为0x0806,也是以大端方式存放在内存中的。


字符的编码(四)_第7张图片
image.png

字符的编码(四)_第8张图片
image.png

其实,IP报文中的很多数据都是需要做字节序转换的,比如包长度、check sum校验和等,这些值大都是short(16bit)或者long(32bit)型,所以解析IP报文时也需要做网络->主机字节序转换,而生成报文字节流时则需要进行主机->网络字节序转换(计算机中的字节序被称之为主机字节序,简称主机序;相对于网络传输中的字节序被称之为网络字节序,简称网络序)。

为了进行网络字节序与主机字节序的转换,BSD sockets(Berkeley sockets)提供了四个转换的函数:htons、ntohs、htonl、ntohl,其中h是host、n是network、s是short、l是long:

htons把unsigned short类型从主机字节序转换到网络字节序

htonl把unsigned long类型从主机字节序转换到网络字节序

ntohs把unsigned short类型从网络字节序转换到主机字节序

ntohl把unsigned long类型从网络字节序转换到主机字节序

在使用little endian的系统中,这些函数会把字节序进行转换;在使用big endian类型的系统中,这些函数会定义成空宏。

Windows系统API中也提供了类似的转换函数。而在.Net中,网络字节序与主机字节序两者之间的转换,由IPAddress类的静态方法提供:HostToNetworkOrder和NetworkToHostOrder。】

Intel和AMD的X86平台,以及DEC(Digital Equipment Corporation,后与Compaq合并,之后Compaq又与HP合并)采用的是Little-Endian,而像IBM、Sun的SPARC采用的就是Big-Endian。有的嵌入式平台是Big-Endian的。JAVA字节序也是Big-Endian的。

当然,这不代表所有情况。有的CPU即能工作于小端,又能工作于大端,比如ARM、Alpha、摩托罗拉的Power PC、SPARC V9、MIPS、PA-RISC和IA64等体系结构(具体情形可参考处理器手册),其字节序是可切换的,这种可切换的特性可以提高效率或者简化网络设备和软件的逻辑。

这种可切换的字节序被称为Bi-Endian(前缀“Bi-”表示“双边的、二重的、两个的”),用于硬件上意指计算机存储时具有可以使用两种不同字节序中任意一种的能力。具体这类CPU是大端还是小端,和具体设置有关。如Power PC可支持Little-Endian字节序,但其默认配置为Big-Endian字节序。

一般来说,大部分用户的操作系统,如windows、FreeBsd、Linux是Little-Endian的;少部分,如Mac OS是Big-Endian的。

具体参见下表:


字符的编码(四)_第9张图片
image.png

所以说,Little Endian还是Big Endian与操作系统和CPU芯片类型都有关系。因此在一个计算机系统中,有可能同时存在大端和小端两种模式的现象。

这一现象为系统的软硬件设计带来了不小的麻烦,这要求系统设计工程师必须深入理解大端和小端模式的差别。大端与小端模式的差别体现在一个处理器的寄存器、指令集、系统总线等各个层次中。

其实很多技术人员在实际的非跨平台桌面应用开发过程中都很少会直接和字节序打交道(因为由硬件直接处理),但在跨平台及网络应用开发过程中因为涉及到异构计算机系统间的通讯交流,字节序是很难回避的问题。

你可能感兴趣的:(字符的编码(四))