Java中Unicode与utf-八、utf-16的关系

转载于:https://www.cnblogs.com/fnlingnzb-learner/p/6163205.html

基础知识:

字符集合(ASCII、Unicode):由一套用于特定用途的字符组成,例如支持西欧语言的字符集合,支持中文的字符集合。字符集合只定义了符号和他们的语意,其实跟计算机没有直接关系。现实生活中,不一样的语系有本身的字符集合,例如藏文有本身的字符集合,汉文有本身的字符集合。到计算机的世界中,也有各类字符集合,例如ASCII字符集合,GB2312字符集合,GBK字符集合。还有一个其余字符集合的超集--Unicode字符集定义了几乎绝大部分现存语言须要的字符,是一种通用的字符集,来支持多语言环境(能够同时处理多种语言混合的状况)。各个国家和地区在制定编码标准的时候,“字符集合”和“字符编码”通常都是同时制定的。因此像ASCII字符集合同样,它也同时表明了一种字符的编码。html

字符编码:是一套规则,定义了在计算机内存中如何表示字符,是字符集中的每一个字符与计算机内存中字节之间的转换关系,也能够认为是把字符数字化,规定每一个“字符”分别用一个字节仍是多个字节存储,用哪些字节来存储。例如ASCII编码[你没看错,它既是一种字符集合,也是一种字符编码],定义了英文字母和符号在计算机中的表示方式,是用一个字节来表示。Unicode字符集合,有好几种字符编码方式,例如变长度编码的UTF8,UTF16等。中文字符集也有不少字符编码,例如上文提到的GB2312编码,GBK编码等。java

char、unicode、string和UTF八、UTF16之间的关系描述:Java语言内部使用的就是16位的Unicode编码,从概念上讲java字符串就是Unicode字符序列,Unicode字符集合的码点(码点:指与一个编码表中的某个字符对应的代码值)能够分红17个代码级别,第一个代码级别称为基本的多语言级别,码点从U+0000到U+FFFF,即65536个码点,只有第一级别的码点能够用一个char值表示;其他的16个级别码点从U+10000到U+10FFFF,其中包含一些辅助字符,须要两个char值才能表示一个码点;Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储,二进制与16进制等能够灵活转换,只是数值的表示方式变化而已,其值的大小不变。网络

UTF八、UTF16即为采用什么样的规则表示全部的码点,在UTF16的编码规则中,UTF16采用不一样长度的编码表示全部的Unicode码点,在基本的多语言级别,每一个字符用16位表示,一般被称为代码单元;而辅助字符采用一对连续的代码单元进行编码。U+D800~U+DFFF为空闲的2048个替代区域,U+D800~U+DBFF用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元,这样的设计能够迅速的知道一个代码单元是一个字符的编码仍是一个辅助字符的第一或第二部分。

性能

为啥须要Unicode

 

      咱们知道计算机其实挺笨的,它只认识0101这样的字符串,固然了咱们看这样的01串时确定会比较头晕的,因此不少时候为了描述简单都用十进制,十六进制,八进制表示.实际上都是等价的,没啥太多不同.其余啥文字图片之类的其余东东计算机不认识.那为了在计算机上表示这些信息就必须转换成一些数字.你确定不能想怎么转换就怎么转,必须得有定些规则.因而刚开始的时候就有ASCII字符集(American Standard Code for Information Interchange, "美国信息交换标准码),它使用7 bits来表示一个字符,总共表示128个字符,咱们通常都是用字节(byte,即8个01串)来做为基本单位.那么怎么当用一个字节来表示字符时第一个bit老是0,剩下的七个字节就来表示实际内容.后来IBM公司在此基础上进行了扩展,用8bit来表示一个字符,总共能够表示256个字符.也就是当第一个bit是0时仍表示以前那些经常使用的字符.当为1时就表示其余补充的字符.编码

        英文字母再加一些其余标点字符之类的也不会超过256个.一个字节表示主足够了.但其余一些文字不止这么多 ,像汉字就上万个.因而又出现了其余各类字符集.这样不一样的字符集交换数据时就有问题了.可能你用某个数字表示字符A,但另外的字符集又是用另一个数字表示A.这样交互起来就麻烦了.因而就出现了Unicode和ISO这样的组织来统一制定一个标准,任何一个字符只对应一个肯定的数字.ISO取的名字叫UCS(Universal Character Set),Unicode取的名字就叫unicode了.spa

      总结起来为啥须要Unicode就是为了适应全球化的发展,便于不一样语言之间的兼容交互,而ASCII再也不能胜任此任务了.操作系统

 

Unicode详细介绍

 

1.容易产生后歧义的两字节设计

        unicode的第一个版本是用两个字节(16bit)来表示全部字符code

        .实际上这么说容易让人产生歧义,咱们总以为两个字节就表明保存在计算机中时是两个字节.因而任何字符若是用unicode表示的话保存下来都占两个字节.其实这种说法是错误的.orm

     其实Unicode涉及到两个步骤,首先是定义一个规范,给全部的字符指定一个惟一对应的数字,这彻底是数学问题,能够跟计算机没半毛钱关系.第二步才是怎么把字符对应的数字保存在计算机中,这才涉及到实际在计算机中占多少字节空间.

     因此咱们也能够这样理解,Unicode是用0至65535之间的数字来表示全部字符.其中0至127这128个数字表示的字符仍然跟ASCII彻底同样.65536是2的16次方.这是第一步.第二步就是怎么把0至65535这些数字转化成01串保存到计算机中.这确定就有不一样的保存方式了.因而出现了UTF(unicode transformation format),有UTF-8,UTF-16.

 

2.UTF-8 与UTF-16的区别

    UTF-16比较好理解,就是任何字符对应的数字都用两个字节来保存.咱们一般对Unicode的误解就是把Unicode与UTF-16等同了.可是很显然若是都是英文字母这作有点浪费.明明用一个字节能表示一个字符为啥整两个啊.

   因而又有个UTF-8,这里的8很是容易误导人,8不是指一个字节,难道一个字节表示一个字符?实际上不是.当用UTF-8时表示一个字符是可变的,有多是用一个字节表示一个字符,也多是两个,三个..反正是根据字符对应的数字大小来肯定.

   因而UTF-8和UTF-16的优劣很容易就看出来了.若是所有英文或英文与其余文字混合,但英文占绝大部分,用UTF-8就比UTF-16节省了不少空间.而若是所有是中文这样相似的字符或者混合字符中中文占绝大多数.UTF-16就占优点了,能够节省不少空间.另外还有个容错问题,等会再讲

 

  看的有点晕了吧,举个例子.假如中文字"汉"对应的unicode是6C49(这是用十六进制表示,用十进制表示是27721为啥不用十进制表示呢?很明显用十六进制表示要短点.其实都是等价的没啥不同.就跟你说60分钟和1小时同样.).你可能会问当用程序打开一个文件时咱们怎么知道那是用的UTF-8仍是UTF-16啊.天然会有点啥标志,在文件的开头几个字节就是标志.

EF BB BF 表示UTF-8

FE FF 表示UTF-16.

 

用UTF-16表示"汉"

假如用UTF-16表示的话就是01101100   01001001(共16 bit,两个字节).程序解析的时候知道是UTF-16就把两个字节当成一个单元来解析.这个很简单.

用UTF-8表示"汉"

用UTF-8就有复杂点.由于此时程序是把一个字节一个字节的来读取,而后再根据字节中开头的bit标志来识别是该把1个仍是两个或三个字节作为一个单元来处理.

0xxxxxxx,若是是这样的01串,也就是以0开头后面是啥就不用管了XX表明任意bit.就表示把一个字节作为一个单元.就跟ASCII彻底同样.

110xxxxx 10xxxxxx.若是是这样的格式,则把两个字节当一个单元

1110xxxx 10xxxxxx 10xxxxxx 若是是这种格式则是三个字节当一个单元.

这是约定的规则.你用UTF-8来表示时必须遵照这样的规则.咱们知道UTF-16不须要用啥字符来作标志,因此两字节也就是2的16次能表示65536个字符.

而UTF-8因为里面有额外的标志信息,全部一个字节只能表示2的7次方128个字符,两个字节只能表示2的11次方2048个字符.而三个字节能表示2的16次方,65536个字符.

因为"汉"的编码27721大于2048了全部两个字节还不够,只能用三个字节来表示.

全部要用1110xxxx 10xxxxxx 10xxxxxx这种格式.把27721对应的二进制从左到右填充XXX符号(实际上不必定从左到右,也能够从右到左,这是涉及到另一个问题.等会说.

刚说到填充方式能够不同,因而就出现了Big-Endian,Little-Endian的术语.Big-Endian就是从左到右,Little-Endian是从右到左.

由上面咱们能够看出UTF-8在局部的字节错误(丢失、增长、改变)不会致使连锁性的错误,由于 UTF-8 的字符边界很容易检测出来,因此容错性较高。

 

Unicode版本2

    前面说的都是unicode的第一个版本.但65536显然不算太多的数字,用它来表示经常使用的字符是没一点问题.足够了,但若是加上不少特殊的就也不够了.因而从1996年开始又来了第二个版本.用四个字节表示全部字符.这样就出现了UTF-8,UTF16,UTF-32.原理和以前确定是彻底同样的,UTF-32就是把全部的字符都用32bit也就是4个字节来表示.而后UTF-8,UTF-16就视状况而定了.UTF-8能够选择1至8个字节中的任一个来表示.而UTF-16只能是选两字节或四字节..因为unicode版本2的原理彻底是同样的,就很少说了.

前面说了要知道具体是哪一种编码方式,须要判断文本开头的标志,下面是全部编码对应的开头标志

EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, little endian
FF FE     UTF-16/UCS-2, big endian
FF FE 00 00  UTF-32/UCS-4, little endian.
00 00 FE FF  UTF-32/UCS-4, big-endian.

其中的UCS就是前面说的ISO制定的标准,和Unicode是彻底同样的,只不过名字不同.ucs-2对应utf-16,ucs-4对应UTF-32.UTF-8是没有对应的UCS


UTF-16 并非一个完美的选择,它存在几个方面的问题:
  1. UTF-16 能表示的字符数有 6 万多,看起来不少,可是实际上目前 Unicode 5.0 收录的字符已经达到 99024 个字符,早已超过 UTF-16 的存储范围;这直接致使 UTF-16 地位颇为尴尬——若是谁还在想着只要使用 UTF-16 就能够高枕无忧的话,恐怕要失望了
  2. UTF-16 存在大小端字节序问题,这个问题在进行信息交换时特别突出——若是字节序未协商好,将致使乱码;若是协商好,可是双方一个采用大端一个采用小端,则必然有一方要进行大小端转换,性能损失不可避免(大小端问题其实不像看起来那么简单,有时会涉及硬件、操做系统、上层软件多个层次,可能会进行屡次转换)
  3. 另外,容错性低有时候也是一大问题——局部的字节错误,特别是丢失或增长可能致使全部后续字符所有错乱,错乱后要想恢复,可能很简单,也可能会很是困难。(这一点在平常生活里你们感受彷佛可有可无,可是在不少特殊环境下倒是巨大的缺陷)
目前支撑咱们继续使用 UTF-16 的理由主要是考虑到它是双字节的,在计算字符串长度、执行索引操做时速度很快。固然这些优势 UTF-32 都具备,但不少人毕竟仍是以为 UTF-32 太占空间了。

反过来 UTF-8 也不完美,也存在一些问题:
  1. 文化上的不平衡——对于欧美地区一些以英语为母语的国家 UTF-8 简直是太棒了,由于它和 ASCII 同样,一个字符只占一个字节,没有任何额外的存储负担;可是对于中日韩等国家来讲,UTF-8 实在是太冗余,一个字符居然要占用 3 个字节,存储和传输的效率不但没有提高,反而降低了。因此欧美人民经常绝不犹豫的采用 UTF-8,而咱们却总是要犹豫一下子
  2. 变长字节表示带来的效率问题——你们对 UTF-8 疑虑重重的一个问题就是在于其由于是变长字节表示,所以不管是计算字符数,仍是执行索引操做效率都不高。为了解决这个问题,经常会考虑把 UTF-8 先转换为 UTF-16 或者 UTF-32 后再操做,操做完毕后再转换回去。而这显然是一种性能负担。


固然,UTF-8 的优势也不能忘了:
  1. 字符空间足够大,将来 Unicode 新标准收录更多字符,UTF-8 也能妥妥的兼容,所以不会再出现 UTF-16 那样的尴尬
  2. 不存在大小端字节序问题,信息交换时很是便捷
  3. 容错性高,局部的字节错误(丢失、增长、改变)不会致使连锁性的错误,由于 UTF-8 的字符边界很容易检测出来,这是一个巨大的优势(正是为了实现这一点,我们中日韩人民不得不忍受 3 字节 1 个字符的苦日子)

那么到底该如何选择呢?

由于不管是 UTF-8 和 UTF-16/32 都各有优缺点,所以 选择的时候应当立足于实际的应用场景。例如在个人习惯中,存储在磁盘上或进行网络交换时都会采用 UTF-8,而在程序内部进行处理时则转换为 UTF-16/32。对于大多数简单的程序来讲,这样作既能够保证信息交换时容易实现相互兼容,同时在内部处理时会比较简单,性能也还算不错。(基本上只要你的程序不是 I/O 密集型的均可以这么干,固然这只是我粗浅的认识范围内的经验,极可能会被无情的反驳)

稍微再展开那么一点点…… 在一些特殊的领域,字符编码的选择会成为一个很关键的问题。特别是一些高性能网络处理程序里更是如此。这时采用一些特殊的设计技巧,能够缓解性能和字符集选择之间的矛盾。例如对于内容检测/过滤系统,须要面对任何可能的字符编码,这时若是还采用把各类不一样的编码都转换为同一种编码后再处理的方案,那么性能降低将会很显著。而若是采用多字符编码支持的有限状态机方案,则既可以无需转换编码,同时又可以以极高的性能进行处理。固然如何从规则列表生成有限状态机,如何使得有限状态机支持多编码,以及这将带来哪些限制,已经又成了另外的问题了。
相关文章
相关标签/搜索