解析Unicode编码和Java char
来源:互联网 发布:eve online for mac 编辑:程序博客网 时间:2024/06/06 03:36
http://chenbochiuan.vip.blog.163.com/blog/static/172332969201072784730795/
Java的字符类型采用的是UTF-16编码方式对Unicode编码表进行表示。其中一个char类型固定2Bytes(16bits)。首先先介绍一下Unicode编码表和UTF-16编码算法:
Unicode编码表的专业术语:
代码点 (code point): 指在Unicode编码表中一个字符所对应的代码值。如汉字“一”的代码点是U+4E00,英文字母“A”的代码点是U+0041。
代码单元( code unit): 规定16bits的存储容量就是一个代码单元。
Unicode编码表 分为17个代码级别 (code plane),其中代码点/-/?为第一级别 ——基本多语言级别 (basic multiling l plane),可以用一个代码单元存储一个代码点。其余16个附加级别 从0x10000-0x10FFFF(需要两个代码单元)。其中需要指出的是在多语言级别中,U+D800-U+DFFF这2048值没有表示任何字符,被称为Unicode的替代区域(surrogate area)。UTF-16正是的运用了这一区域,用2个代码单元(2*16bits)巧妙的表示出20bits代码点的Unicode附加级别。
UTF-16编码算法
假设U是一个代码点,也就是Unicode编码表中一个字符所对应的Unicode值。
1) 如果U<U+10000,也就是处于Unicode的基本多语言级别中。这样16bits(一个代码单元)就足够表示出字符的Unicode值。
2) 如果U+10FFFF>U>=U+10000,也就是处于附加级别中。UTF-16用2个16位来表示出了,并且正好将每个16位都控制在替代区域U+D800-U+DFFF中了,具体操作如下:
分别初始化2个16位无符号的整数 —— W1和W2。其中W1=110110yyyyyyyyyy(0xD800-0xDBFF),W2 = 110111xxxxxxxxxx(0xDC00-OxDFFF)。然后,将U的高10位分配给W1的低10位,将U的低10位分配给W2的低10位。这样就可以将20bits的代码点U拆成两个16bits的代码单元。而且这两个代码点正好落在替代区域U+D800-U+DFFF中。
具体举个例子:代码点U+1D56B(一个整数集的算术符号Z)
0x1D56B= 0001 1101 0101 0110 1011
将0x1D56B的高10位0001 1101 01分配给W1的低10位组合成110110 0001 1101 01=0xD875
将0x1D56B的低10位01 0110 1011分配给W2的低10位组合成110111 01 0110 1011=0xDD6B
这样代码点U+1D56B采用UTF-16编码方式,用2个连续的代码单元U+D875和U+DD68表示出了。
Java的char类型是固定16bits的。代码点在U+0000 — U+FFFF之内到是可以用一个char完整的表示出一个字符。但代码点在U+FFFF之外的,一个char无论如何无法表示一个完整字符。这样用 char类型来获取字符串中的每一个字符就有问题了(针对那些含有代码点在U+FFFF之外的字符串)。我们还用U+1D56B举个例子:
假设程序中有一个字符串String str = new String(Character.toChars(0x1D56B)),str只有一个字符。注意我们不能用转义字符'/?'来表示,' /uXXXX'只能表示四位16进制数,也就是只能表示基本多语言级别的Unicode代码点。对于附加级别的代码点,我们只能用上面那种形式表示。
此时,char ch=str.charAt(0); 这个时候我们返回的结果是第一个代码单元,也就是代码点0xD875(上面已经讲过了U+1D56B采用UTF-16编码方式分成了U+D875和 U+DD6B,这样就需要两个char的容量来存储这个字符)。这种方法我们永远无法得到字符串中的这个附加级别字符。
我们可以这样来精确表示整个字符串中的每一个字符
int cp=str.codePointAt(0);
if(Charcter.isSupplementaryCodePoint(cp)) //判断Unicode 代码点是否在附加级别字符范围内。
i+=2;
else i++;
另外,String类中的length()方法也是对字符串进行char统计,也就是计算代码单元数量(代码单元有可能大于正真的字符数量)。如果要计算代码点数量,必须用str.codePointCount(0,str.length())方法。
由此可见,用char类型来处理字符串在有些情况下是不准确的,我们最好使用字符串类型中处理代码点的方法,不要使用处理char类型(一个代码单元)的方法。
我顺便提一下,虽然我们的很多系统支持Unicode编码表,但是这并不意味这我们能显示一些字符。比如要显示上面那个字符串 System.out.println(str);显示的就是一个'?',这主要是因为字符的显示需要有显示字库的支持,否则还是屏幕上什么都看不到。
附:有这样一个问题
char c='编'; // 我们都知道c占用2个字节,这毫无疑问。
String str=“遍”;
byte[] bytes=str.getBytes(); //这个打印出来,在不同的操作系统上可能有不同的长度。为什么呢?
我们可以去看看API, String 的getBytes()方法使用平台的默认字符集将此 String 编码为 byte 序列 。关于默认的字符集可以看看这篇文章《Java字符串与字符集的基本概念 》。
也就是说,你当前的OS默认支持的字符集使用的是UTF-8,则是3个字节。如果是gb2312/gbk,则2个字节。如果使用unicode/UTF- 16则4个字节(开头的两个字节是一个mark)。
千万不要认为char的占用字节数变了,注意Java标榜自己的一个重要性能就是平台无关性。固定长度的基本数据类型在Java中是永远占固定长度的,这一点和C有很大区别。
- 正确解析Unicode编码和Java char
- 解析Unicode编码和Java char 类型
- 解析Unicode编码和Java char 类型
- 解析Unicode编码和Java char
- 解析Unicode编码和Java char
- 解析Unicode编码和Java char
- 解析Unicode编码和Java char
- java中的char类型和Unicode编码
- java中的char类型和Unicode编码
- java中的char类型和Unicode编码
- java 中char类型和 Unicode编码
- 解析Unicode编码和Java char(转自Oilamp的163博客)
- char类型和Unicode编码
- char类型与Unicode编码
- char类型与Unicode编码
- char类型与Unicode编码
- char类型与Unicode编码
- Android解析UniCode编码
- 关于CAShapeLayer的一些实用案例和技巧
- include动作与指令的区别
- 字典树排序(思路分析)
- NIO就这些知识吗?
- 多线程学习(一)什么是多线程?
- 解析Unicode编码和Java char
- mportError: No module named _tkinter, please install the python-tk package
- 路由协议——PDU的传播方式对比
- Android Multiple Selection Spinner
- 网易编程
- JavaScript高级程序设计(第1章 JavaScript简介)
- springboot 中定时任务cron表达式
- MyBatis学习路上打的那些码(五、MyBatis新特性:映射器(mapper))
- java中的类修饰符、成员变量修饰符、方法修饰符。