Unicode 字符和UTF编码的理解
Unicode 编码的由来
我们都知道,计算机的内部全部是由二进制数字0, 1 组成的, 那么计算机就没有办法保存我们的文字, 这怎么行呢? 于是美国人就想了一个办法(计算机是由美国人发明的),也把文字转化成数字,计算机不就能够保存文字了,所以美国人就制定了一张表,规定了文字与数字的一一对应,字符A 就对应数字65, 字符B 就对应数字66, 这张表就是著名的ASCII 码表。由于美国人的文字比较少,就是a, b, c d 等等, 对应完了,发现一共使用了128个数字,这也太少了,一个字节都没有使用了,所以就决定用一个字节来表示一个字符, 所以对于ASCII 码来说,一个字符在计算机中就占用一个字节。码表制定好了,生产计算机的时候直接把码表内置到计算机中就可以了。
但是随着计算机的推广,它到达了欧洲,亚洲,这就有点问题了,因为计算机中只有英文,它不可能表达 和书写其他国家的语言,比如汉语,日语等等, 这肯定也是不行的。于是各个国家的政府就制定各自的码表, 好让计算机也能表示本国的语言,就拿我国来说吧,GBK, GB2313 码表就出现了。 这就会出现一个问题,相同的数字在不同的码表中对应的文字可能不同, 这就有可能造成乱码。国际友人发了一封电子邮件过来,打开一看乱码了,各国之间的文件不能够交换使用。这时国际标准化组织就想把各国的字符都统一起来,把它们放到一张码表中,如果计算机中都内置这张表,那就不会出现乱码了。和ASCII码的想法一致,在这张表中,也是给每一个字符都分配一个独一无二的数字。这张表就是Unicode码表或Unicode编码字符集,这每一个字符对应的数字称之为做码点(Code Point). 码表的样子如下
Unicode 字符集在计算机中的实现
Unicode 字符集的基础非常简单,就是给世界上的每一个文字都分配一个独一无二的数字,这个数字称之为码点(code point), 比如 给字符A分配的数字是65, 给字符B 分配的数字是66,那么 A的 码点(code point)就是65, B 的code point 就是66. 但Unicode 相对于其他编码又是非常复杂的,这主要是在于Unicode 字符在计算机中的实现上,这些数字怎么在计算机中表示,用多少个字节?
由于当时提出制定Unicode字符集标准的时候,是在1990s 左右,那时各国的文字都比较少,都对应完了之后,发现并没有超过6万,正好在计算机中,两个字节就可以表示6万多个字,两个字节就是16个位(bit),无符号的话那么最大的数就是16个位都是1,这个数就是 2的16次方即 2^16-1 = 65536-1 = 65535所以就决定用2个字节,16个bit 来表示Unicode 字符, 这就是最早的UCS-2编码, 这16个bit 称之为code uint(码元), 这时 一个码点就对应一个码元。
但是随着时间的推移,尤其是亚洲国家把大量的象形文字也加入到Unicode 编码中,突然发现,65536的数量根本就装不下这些文字,于是Unicode 编码字符集也相应的进行了扩充,它把整个字符集分成了17个平面,每一个平面都能放2^16 个文字, 第一个平面就放置最初的UCS-2编码中所定义的文字,叫做基本多文本平面Basic Multilingual Plane (or BMP), 所以基本多文本平面码点的取值范围是0x0000 - 0xFFFF, 其他的16个平面称为补充平面(Supplementary Planes or Astral Planes), 它们的取值范围从0x10000 开始 (0xFFFF + 1 就等于0x10000, 16进制的计算) 。 针对这种多平面的字符集,出现了不同的在计算机中的实现方式,那就是我们经常听到的UTF-8, UTF-16, UTF-32.
UTF-16 就是对于在BMP 平面的文字用两个字节(16个bit)进行表示,然后对于其他平面的文字用四个字节进行表示,四个字节表示称之为surrogate pairs(代理对), 一对16 bit 来表示一个 字符。 由于一个16bit 称之为码元code unit, 一个字符对应的数字称为码点,这也就意味着,一个码点要有2个码元进行表示。
那怎么才能让一个码点用两个码元来显示呢? 先看一下目前Unicode 非基本面的有多少个字, 一共16个面, 一个面是2^16 个字,那也就是说,一共有16 * 2^16 个字, 就是2^20个字符, 也就需要20个二进制bit 位,一个码元只能16bit 位,那只能把20bit 位进行拆分成2个部分,一个部分占10个bit, 这样一个码元就可以表示了。但这又存在另外一个问题,当计算机去读取字符的时候,它碰到一个字节就去读取一个字节,它并不知道,你这个字节是拆分出来的,你会发现,本来一个字,确显示出了毫不相干的两个字,这怎么办呢?其实在usc-2 的时代,它所定义的Unicode 字符集并没有完全使用了2个字节,2个字节的后面一部分是空的,并没有对应任何字符,这一段是0xD800 到 0xDFFF(55296 - 65535), 这也就意味着,使用这一段上的码元没有任何问题。那我们就想办法,让非基本面的码点对应到这一段上来。对应的逻辑是这样的,首先把0xD800 - 0xDFFF 分为两个部分,0xD800 到 0xDBFF 和0xDC00 到 0xDFFF,然后再用码点减去65536, 把剩下的数用二进制表示,如果二进制不够20位,可以在前面补0. 再把20位二进制分为两个部分,每个部分占用10个bit, 前10个bit 对应到0xD800 - 0xDFFF, 称为高位(H), 后10个bit 对应到0xDC00 到 0xDFFF 称为低位(L), 这样一个码点就可以用两个码元(高位, 低位)进行表示了。映射比较复杂,不过Unicode 提供了一个公式,可以直接通过码点得到高低位码元。
H = Math.floor((c-0x10000) / 0x400)+0xD800 // c 是码点 0x10000 就是65536 0x400 就是1024, 0xD800 就是H 开始位置 L = (c - 0x10000) % 0x400 + 0xDC00 // 0xDC00 L 位开始的位置。
举一个小栗子,汉字"
Unicode 字符和UTF编码的理解的更多相关文章
- Java 字符编码(一)Unicode 字符编码
Java 字符编码(一)Unicode 字符编码 Unicode(http://www.unicode.org/versions/#TUS_Latest_Version) 是一个编码方案,说白了希望给 ...
- 谈谈对Java中Unicode、编码的理解
我们经常会遇到编码问题.Java号称国际化的语言,是因为它的class文件采用UTF-8,而JVM运行时使用UTF-16(至于为什么JVM中要采用UTF-16,我没看过 相关的资料,但我猜可能是因为J ...
- UNICODE UTF编码方式解析
先明确几个概念 基础概念部分 1.字符编码方式CEF(Character Encoding Form) 对符号进行编码,便于处理与显示 常用的编码方式有 GB2312(汉字国标码 2字节) ASCII ...
- 转载一篇关于unicode字符编码的文章
很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们认为8个开关状态作为原子单位很好,于是他们把这称为"字节". 再后来,他们又做了一 ...
- 从ord()中对Unicode编码的理解
刚开始学习编程的时候,老对字符串编码的理解模模糊糊.也一直看这方便的资料,今天在看Dive in python时,突然有了新的理解(不知道是否正确). Python有个built-in函数ord(), ...
- decode 函数将字符串从某种编码转为 unicode 字符
环境:Ubuntu, Python 2.7 基础知识 这个程序涉及到的知识点有几个,在这里列出来,不详细讲,有疑问的直接百度会有一堆的. 1.urllib2 模块的 request 对像来设置 HTT ...
- 关于JAVA字符编码:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换
我们最初学习计算机的时候,都学过ASCII编码. 但是为了表示各种各样的语言,在计算机技术的发展过程中,逐渐出现了很多不同标准的编码格式, 重要的有Unicode.UTF.ISO-8859-1和中国人 ...
- ASCII, Unicode, UTF-8, 8进制, 16进制等各种编码学习理解笔记
字符编码的发展历史 Unicode和UTF-8有何区别? 在这个问题下的于洋的最高票回答中,比较完整地介绍了字符编码的发展历史,为了便于记忆,再次简要概括一番. 一个字节:最初一个字节的标准是混乱的, ...
- 初学者对ASCII编码、Unicode编码、UTF-8编码的理解
最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是 255(二进制 11111111=十进制 255),如果要表示更大的整数,就必须用更多的字节. ...
随机推荐
- Proj.4 升级新版本5.x和6.x
目录 Proj.4 升级新版本5.x和6.x 0.缘起 1.5.x和6.x更新情况简述 PROJ 5.x 更新 PROJ 6.x 更新 2.从PROJ.4向新版本迁移 迁移到5.x版本 迁移到6.x版 ...
- jcenter下载不了时,用国内镜像下载解决
修改build.gradle,以下是原本的配置信息 buildscript { repositories { google() jcenter() } dependencies { classpath ...
- 【Python】解析Python中的异常操作
目录结构: contents structure [-] try,except,else,finally块 异常处理 使用except而不带任何异常类型 使用except而带多种异常类型 try-fi ...
- js---省略花括号{}的几种表达式
在进行js的书写中,对于常见的if,for,while是可以简写,省略花括号{}的: var a = 10,b = 20; /** * if 简写 */ if(a > b) console.lo ...
- idel上传代码到github时遇到的Push rejected: Push to origin/master was rejected
1 没有权限 2 先pull之后,再push即可
- 【HBase】HBase 单机版安装及使用
HBase介绍 HBase是一个分布式的.面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”.就像Bigtable利用了 ...
- zxing解析生成一维码二维码
@web界面实现扫一扫 二维码工具类 package util; import java.awt.BasicStroke; import java.awt.Graphics; import java. ...
- spring boot 指定启动端口
spring boot 默认端口为8080 1.修改为指定端口 (1)修改配置文件 src/main/resources/application.properties server.port= (2) ...
- spark + hive
1.如何让 spark-sql 能够访问hive? 只需将hive-site.xml 放到 spark/conf 下即可,hive-site.xml 内容请参照hive集群搭建 2.要在spark 代 ...
- 协程介绍, Greenlet模块,Gevent模块,Genvent之同步与异步
昨日内容回顾 I/O模型,面试会问到I/O操作,不占用CPU.它内部有一个专门的处理I/O模块.print和写log 属于I/O操作,它不占用CPU 线程GIL保证一个进程中的多个线程在同一时刻只有一 ...