浅谈Java中Unicode的编码和实现

Unicode的编码和实现

大概来说,Unicode编码系统可分为编码方式和实现方式两个层次。

编码方式

字符是抽象的最小文本单位。它没有固定的形状(可能是一个字形),而且没有值。“A”是一个字符,“€”也是一个字符。字符集是字符的集合。编码字符集是一个字符集,它为每一个字符分配一个唯一数字。

Unicode 最初设计是作为一种固定宽度的 16 位字符编码。也就是每个字符占用2个字节。这样理论上一共最多可以表示216(即65536)个字符。上述16位统一码字符构成基本多文种平面。基本多文种平面的字符的编码为U+hhhh,其中每个h代表一个十六进制数字。

很明显,16 位编码的所有 65,536 个字符并不能完全表示全世界所有正在使用或曾经使用的字符。于是,Unicode 标准已扩展到包含多达 1,112,064 个字符。那些超出原来的 16 位限制的字符被称作增补字符。Unicode 标准 2.0 版是第一个包含

启用增补字符设计的版本,但是,直到 3.1 版才收入第一批增补字符集。

Unicode字符平面映射

目前的Unicode字元分为17组编排,每组称为平面(Plane),而每平面拥有65536(即216)个代码点。然而目前只用了少数平面。

平面 始末字元值 中文名称 英文名称
0号平面 U+0000 - U+FFFF 基本多文种平面 Basic Multilingual Plane,简称BMP
1号平面 U+10000 - U+1FFFF 多文种补充平面 Supplementary Multilingual Plane,简称SMP
2号平面 U+20000 - U+2FFFF 表意文字补充平面 Supplementary Ideographic Plane,简称SIP
3号平面 U+30000 - U+3FFFF 表意文字第三平面(未正式使用) Tertiary Ideographic Plane,简称TIP
4号平面

13号平面
U+40000 - U+DFFFF (尚未使用)  
14号平面 U+E0000 - U+EFFFF 特别用途补充平面 Supplementary Special-purpose Plane,简称SSP
15号平面 U+F0000 - U+FFFFF 保留作为私人使用区(A区) Private Use Area-A,简称PUA-A
16号平面 U+100000 - U+10FFFF 保留作为私人使用区(B区) Private Use Area-B,简称PUA-B

增补字符是代码点在 U+10000 至 U+10FFFF 范围之间的字符(上述表格中1号平面~16号平面之间的),也就是那些使用原始的 Unicode 的 16 位设计无法表示的字符。从 U+0000 至 U+FFFF 之间的字符集有时候被称为基本多语言面 (BMP)。因此,每一个 Unicode 字符要么属于 BMP,要么属于增补字符。

实现方式

UTF-32、UTF-16 和 UTF-8 是具体的实现方案。Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)。

例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大幅节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。

再如,如果直接使用与Unicode编码一致(仅限于BMP字符)的UTF-16编码,由于每个字符占用了两个字节,在麦金塔电脑(Mac)机和个人电脑上,对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如某字符为十六进制编码4E59,按两个字节拆分为4E和59,在Mac上读取时是从低字节开始,那么在Mac OS会认为此4E59编码为594E,找到的字符为“奎”,而在Windows上从高字节开始读取,则编码为U+4E59的字符为“乙”。就是说在Windows下以UTF-16编码保存一个字符“乙”,在Mac OS环境下打开会显示成“奎”。此类情况说明UTF-16的编码顺序若不加以人为定义就可能发生混淆,于是在UTF-16编码实现方式中使用了大端序(Big-Endian,简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE)的概念,以及可附加的字节顺序记号解决方案,目前在PC机上的Windows系统和Linux系统对于UTF-16编码默认使用UTF-16 LE。(具体方案参见UTF-16)

此外Unicode的实现方式还包括UTF-7、Punycode、CESU-8、SCSU、UTF-32、GB18030等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是UTF-16小端序(LE)、UTF-16大端序(BE)和UTF-8。在微软公司Windows XP附带的记事本(Notepad)中,“另存为”对话框可以选择的四种编码方式除去非Unicode编码的ANSI(对于英文系统即ASCII编码,中文系统则为GB2312或Big5编码)外,其余三种为“Unicode”(对应UTF-16 LE)、“Unicode big endian”(对应UTF-16 BE)和“UTF-8”。

代码点、码位

在字符编码术语中,码位或称编码位置,即英文的code point或code position,是组成码空间(或代码页)的数值。 例如,ASCII码包含128个码位,范围是016进制到7F16进制,扩展ASCII码包含256个码位,范围是016进制到FF16进制,而Unicode包含1,114,112个码位,范围是016进制到10FFFF16进制。Unicode码空间划分为17个Unicode字符平面(基本多文种平面,16个辅助平面),每个平面有65,536(= 216)个码位。因此Unicode码空间总计是17 × 65,536 = 1,114,112.

代码单元、码元

码元(Code Unit,也称“代码单元”)是指一个已编码的文本中具有最短的比特组合的单元。对于UTF-8来说,码元是8比特长;对于UTF-16来说,码元是16比特长;对于UTF-32来说,码元是32比特长。码值(Code Value)是过时的用法。
明白了上述两个概念,我们就可以认为UTF-N(N为8,16,32)干的事就是把Unicode字符集的抽象码位映射为N位长的整数(即码元)的序列,用于数据存储或传递。

UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数。很明显,它是内部处理最方便的表达方式,但是,如果作为一般字符串表达方式,则要消耗更多的内存。

UTF-16 使用一个或两个未分配的 16 位代码单元的序列对 Unicode 代码点进行编码。值 U+0000 至 U+FFFF 编码为一个相同值的 16 位单元。增补字符编码为两个代码单元,第一个单元来自于高代理范围(U+D800 至 U+DBFF),第二个单元来自于低代理范围(U+DC00 至 U+DFFF)。这在概念上可能看起来类似于多字节编码,但是其中有一个重要区别:值 U+D800 至 U+DFFF 保留用于 UTF-16;没有这些值分配字符作为代码点。这意味着,对于一个字符串中的每个单独的代码单元,软件可以识别是否该代码单元表示某个单单元字符,或者是否该代码单元是某个双单元字符的第一个或第二单元。这相当于某些传统的多字节字符编码来说是一个显著的改进,在传统的多字节字符编码中,字节值 0x41 既可能表示字母“A”,也可能是一个双字节字符的第二个字节。

UTF-8 使用一至四个字节的序列对编码 Unicode 代码点进行编码。U+0000 至 U+007F 使用一个字节编码,U+0080 至 U+07FF 使用两个字节,U+0800 至 U+FFFF 使用三个字节,而 U+10000 至 U+10FFFF 使用四个字节。UTF-8 设计原理为:字节值 0x00 至 0x7F 始终表示代码点 U+0000 至 U+007F(Basic Latin 字符子集,它对应 ASCII 字符集)。这些字节值永远不会表示其他代码点,这一特性使 UTF-8 可以很方便地在软件中将特殊的含义赋予某些 ASCII 字符。

下表所示为几个字符不同表达方式的比较:

Unicode 代码点 U+0041 U+00DF U+6771 U+10400
表示字形
UTF-32 代码单元
00000041
000000DF
00006771
00010400
UTF-16 代码单元
0041
00DF
6771
D801 DC00
UTF-8 代码单元
41
C3 9F
E6 9D B1
F0 90 90 80

注:上述编码中的数字均是十六进制表示的。

总结

以上就是本文关于浅谈Java中Unicode的编码和实现的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Java编程将汉字转Unicode码代码示例、Java源码解析之object类等,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

时间: 2017-10-22

java解析php函数json_encode unicode 编码问题

android开发中在和服务器端接口对接时出现编码问题,从服务器端获取到的数据是 "\u8bbe\u59071ID-\u8bbe\u59071\u540d\u79f0;\u8bbe\u59073id-\u8bbe\u59073\u540d\u79f0;\u8bbe\u59077id-\u8bbe\u59077\u540d\u79f0" 接口是通过php函数中json_encode进行编码后返回的,在客户端通过java.net.URLdecoder.decode()解码不管用,但是直接将

js unicode 编码解析关于数据转换为中文的两种方法

复制代码 代码如下: var str = "\\u6211\\u662Funicode\\u7F16\\u7801"; 关于这样的数据转换为中文问题,常用的两种方法. 1. eval 解析 复制代码 代码如下: str = eval("'" + str + "'"); // "我是unicode编码" 2. unescape 解析 复制代码 代码如下: str = unescape(str.replace(/\\u/g, &q

Java字符串替换函数replace()用法解析

这篇文章主要介绍了Java字符串替换函数replace()用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 replace(char oldChar, char newChar)返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 而生成的 代码如下 import java.util.*; public class Main{ public static void main(String[] args)

Java编程将汉字转Unicode码代码示例

上一次接触到编码的知识,还是上大学的时候,那时候学的是通信工程专业,有关编码的内容,不记得是在通信原理还是信息论与编码里面学到的了.却依然记得那个信息论与编码的老师,最喜欢吃的是尖椒肥肠盖饭,不知道是尖椒肥肠吃多了还是太聪明的缘故,三十多岁就开始拜顶了.那四年真是一段难忘的回忆... 话不多说,咱们进入正题.这里是一个简单的Java编程将汉字转Unicode码代码示例,下面是代码: package me.socketthread; public class ToUnicode { /** * @

PHP解码unicode编码的中文字符代码分享

问题背景: 晚上在抓取某网站数据,结果在数据包中发现了这么一串编码的数据:"......\u65b0\u6d6a\u5fae\u535a......", 这其实是中文被unicode编码后了的数据,我现在就是想解码出中文来,上度娘搞了半天,试了很多的姿(方)势(法),终于搞定了. 解决方案: 呵呵,老外就是给力啊, 猛戳这里看老外给的解决方案 方案A(稳定版+推荐): function replace_unicode_escape_sequence($match) { return m

C++11 Unicode编码转换

1.char16_t与char32_t 在C++98中,为了支持Unicode字符,使用wchar_t类型来表示"宽字符",但并没有严格规定位宽,而是让wchar_t的宽度由编译器实现,因此不同的编译器有着不同的实现方式,GNU C++规定wchar_t为32位,Visual C++规定为16位.由于wchar_t宽度没有一个统规定,导致使用wchar_t的代码在不同平台间移植时,可能出现问题.这一状况在C++11中得到了一定的改善,从此Unicode字符的存储有了统一类型: (1)c

跟我学习javascript的var预解析与函数声明提升

1.var 变量预编译 JavaScript 的语法和 C .Java.C# 类似,统称为 C 类语法.有过 C 或 Java 编程经验的同学应该对"先声明.后使用"的规则很熟悉,如果使用未经声明的变量或函数,在编译阶段就会报错.然而,JavaScript 却能够在变量和函数被声明之前使用它们.下面我们就深入了解一下其中的玄机. 先来看一段代码: (function() { console.log(noSuchVariable);//ReferenceError: noSuchVari

java解析XML几种方式小结

java解析XML几种方式小结 第一种:DOM. DOM的全称是Document Object Model,也即文档对象模型.在应用程序中,基于DOM的XML分析器将一个XML文档转换成一个对象模型的集合(通常称DOM树),应用程序正是通过对这个对象模型的操作,来实现对XML文档数据的操作.通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此,这种利用DOM接口的机制也被称作随机访问机制. DOM接口提供了一种通过分层对象模型来访问XML文档信息的方式,这些分层对象模型依

php的mail函数发送UTF-8编码中文邮件时标题乱码的解决办法

最近遇到一个问题,就是在使用php的mail函数发送utf-8编码的中文邮件时标题出现乱码现象,而邮件正文却是正确的.最初以为是页面编码的问题,发现页面编码utf-8没有问题啊,找了半天原因,最后找到了问题所在. 1.使用 PEAR 的 Mail 类 PEAR 的 Mail 类可以让你选择使用 sendmail 或者 SMTP 方式发信,这样的包装好的接口很好用,你没有必要去自己重新发明轮子. 2.关于headers 中subject 的乱码 不要把任何除了 iso-8859-1 编码之外的文字

程序员趣味读物 谈谈Unicode编码

问题一: 使用Windows记事本的"另存为",可以在GBK.Unicode.Unicode big endian和UTF-8这几种编码方式间相互转换.同样是txt文件,Windows是怎样识别编码方式的呢? 我很早前就发现Unicode.Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF.FE(Unicode),FE.FF(Unicode big endian),EF.BB.BF(UTF-8).但这些标记是基于什么标准呢? 问题二: 最近