JAVA如何正确处理Unicode字符

最近在开发输入法程序时遇到一个小问题,就是删除一个emoji时,不能一次删干净,需要执行两次操作才可以。Intuitively,这肯定是java操作unicode字符的问题,于是找了JAVA官方文档参考一下,解决了这个问题,这里做下简单总结。原文在这里,有兴趣自己看。

http://www.oracle.com/technetwork/articles/java/supplementary-142654.html

注:文章中提到的“JAVA字节”均指JAVA平台的16位字节,请不要和C的8位字节搞混。

首先需要知道标准Unicode字符是一个16位字符(废话),所以标准Unicode字符集包含65536个字符(2的16次方嘛,哥的数学很好的)。但是仅仅65536个字符是远远不够用的,尤其是为了包含我们伟大中华民族丰富多彩的文字(反正我是不认识),Unicode字符集进行了扩展,扩展到了24位,也就是最多可以包含1,112,064个字符。这里我们把标准Unicode字符集(也就是前65536个字符)叫做Basic Multilingual Plane (BMP),把超过16位以上的扩展字符集叫做supplementary characters。

UTF-16是一种编码方式,以16位无符号单元来编码Unicode字符,如果对一个标准Unicode字符编码,只需要占用一个UTF-16单元,如果对扩展Unicode字符编码则需要占用两个UTF-16单元。

我们都知道在C语言中,一个primitive char占用一个字节,也就是8位。但是在JAVA中,一个primitive char占用16位,与一个标准Unicode字符长度相等,因为JAVA平台采用UTF-16进行编码。这样JAVA就很容易处理Unicode基本字符。但是对于Unicode的扩展字符,在JAVA中就需要占用两个char,也就是两个UTF-16单元。

举个栗子,大写字母A的Unicode值为U+0041,它属于Unicode的基本字符集,所以它只占用一个UTF-16单元,表示为[0041]。而字符的Unicode值为U+10400,它属于扩展Unicode字符集,所以它占用两个UTF-16编码单元,表示为[D801][DC00](一个中括号代表一个UTF-16单元,中括号本身没意义)。第一个单元叫做high-surrogates,范围从 U+D800 到 U+DBFF,第二个单元叫low-surrogates,范围从 U+DC00 到 U+DFFF,这个看起来很类似多字节编码。但是,我说的是但是,这里有一个非常重要的不同点,从U+D800 到 U+DFFF 其实为UTF-16的保留值范围,专门用作编码Unicode扩展字符集,这个范围不被赋予任何实际的标准Unicode字符。也就是说,在你的程序里只要判断一个字符是否属于这个范围内,就可以知道这个字符是应该被当做一个独立的Unicode基本字符处理,还是当做半个扩展Unicode字符。

回到我一开始提到问题,我在开发android输入法的时候,当需要删除一个emoji时,总是需要删除两次才能删干净。这是因为我用的emoji都是属于Unicode扩展字符集的,在编辑框中占用了两个UTF-16单元,而每执行一次删除操作只删除了一个UTF-16单元,也就是一个JAVA字节,而另一个单元没有被删除,所以就会显示异常。这时候只要再删除掉另一个字节就算真正把这个emoji删除干净了。

用户在实际的应用中,肯定是把Unicode标准字符集和扩展字符集混合着使用,也就是说有的字符占用1个java字节,有的字符占用两个java字节。那么我在执行删除一个字符的操作时必须也要先去判断我是要一次性删除一个字节还是删除两个字节。这就很简单了,按照我前面提到的规则,先判断一下被操作的字符值是否处于[U+D800,U+DFFF](inclusive)之间,如果是,就说明被操作的字符不是一个有效的标准Unicode字符,而是半个Unicode扩展字符,那么只需要在程序中自动删除两个java字节就可以了。反之,如果被处理的字符不属于[U+D800,U+DFFF]范围内,那么这个字符就是一个标准的Unicode字符,只需要按照一个java字节处理就可以了。

理论清楚后(我假设你清楚了,其实也不难,就是我表达的不清楚,没办法,工科人,总是思维超越语言),我再简单介绍一下java中相关的处理函数。


  • Character.toChars(int codePoint):参数codePoint为Unicode值,此函数将Unicode值转换为标准java字节数组。如果codePoint是Unicode标准字符,则返回值只包含一个char;如果codePoint是扩展Unicode字符,则返回值包含2个char。

    例:在程序中如果要输出一个Unicode扩展字符,可以这样String.valueOf(Character.toChars(0x1F60E))

  • Character.isLowSurrogate(char ch):判断一个字符是否是一个Unicode扩展字符的低16位编码。
  • Character.isHighSurrogate(char ch):判断一个字符是否是一个Unicode扩展字符的高16位编码。
时间: 2024-12-21 03:51:50

JAVA如何正确处理Unicode字符的相关文章

Java 经典实例: Unicode字符和String之间的转换

在Java诞生之际,Unicode码是一个16位的字符集,因此char值似乎顺其自然为16位宽,多年来一个char变量几乎可以表示任何Unicode字符. /** * Created by Frank */ public class UnicodeChars { public static void main(String[] args) { StringBuilder b = new StringBuilder(); for (char c = 'a'; c < 'd'; c++) { b.a

【整理】Python中实际上已经得到了正确的Unicode或某种编码的字符,但是看起来或打印出来却是乱码

转自:http://www.crifan.com/python_already_got_correct_encoding_string_but_seems_print_messy_code/ [背景] Python中的字符编码,其实的确有点复杂. 再加上,不同的开发环境和工具中,显示的逻辑和效果又不太相同,尤其是,中文的,初级用户,最常遇到的: (1)在Python自带的IDE:IDLE中折腾中文字符,结果看到的差不多都是乱码类的东西,比如:’\xd6\xd0\xce\xc4′ (2)将一个中文

Java判断中文及中文字符转unicode

1.java中判断字符是否为中文 /** * 判断是否为中文字符 * @param c * @return */ public boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIB

ECMAScript6面对大于0xFFFF的Unicode字符如何正确返回长度

一.match() 1.定义 match()方法用于检索字符串内指定(字符串或正则)的值,返回指定值的数组,若找不到,返回null. 2.语法 str.match(searchvalue) str.match(regexp) 3.示例 let str = 'abc-def-zxc'; console.log(str.match('-')); // ["-", index: 3, input: "abc-def-zxc"] console.log(str.match(

在Python中正确使用Unicode

正确处理文本,特别是正确处理Unicode.是个老生常谈的问题,有时甚至会难倒经验丰富的开发者.并不是因为这个问题很难,而是因为对软件中的文本,开发者没有正确理解一些关键概念及其表示方法.在StackOverflow上搜索关于UnicodeDecodeError相关的问题,可以看到很多人都有这样的误解.这些错误的概念可以追溯到Unicode出现之前.那时许多现今的开发者还没入职,也包括我自己.如果这些错误的概念没有散布开来,其实不是个问题.现在很多人都有这些错误概念,部分原因是因为有些非常流行的

MySQL字符编码的讨论:如何处理emoji等4字节的Unicode字符 - utf8mb4 vs. utf8 Collations

1. Unicode是什么 Unicode(中文:万国码.国际码.统一码.单一码)是计算机科学领域里的一项业界标准.它对世界上大部分的文字系统进行了整理.编码,使得电脑可以用更为简单的方式来呈现和处理文字. 简单说来,就是把世界上所有语言的字,加上所有能找到的符号(如高音谱号.麻将.emoji)用同一套编码表示出来. 2. UTF-8是什么 UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码.可变长度的意思在于,如果能使

Java基本的程序结构设计 字符类型

字符型 从概念上讲,java的字符型就是unicode字符序列. 不可变: 一旦定义了一个字符串,就没有方法修改它.java没有提供修改字符串的方法,对于C程序来说,字符串相当于是个数组,你可以改变任何一个下标的值.但是Java不可以. 如果要对java字符串修改下标,只能新建一个字符串,然后通过substring和拼接来实现,这样一定程度上效率很低. 但是java设计者认为字符串修改操作很少,对于字符串的操作,大多是比较,和合并等操作.所以java设计者将字符串设置为不可变,然后实现了共享.

Unicode字符集和多字节字符集关系(转载)

Unicode字符集和多字节字符集关系 原文链接:http://blog.csdn.net/stephen1315/article/details/ 在计算机中字符通常并不是保存为图像,每个字符都是使用一个编码来表示的,而每个字符究竟使用哪个编码代表,要取决于使用哪个字符集(charset).      在最初的时候,Internet上只有一种字符集——ANSI的ASCII字符集,它使用7 bits来表示一个字符,总共表示128个字符,其中包括了英文字母.数字.标点符号等常用字符.之后,又进行扩

[总结]Perl在遇到Unicode字符文件名时的各种处理方法

环境 XP/WIN7  Perl v5.16 编辑整理:523066680 常见的那些文件操作函数都不支持,于是为了达到目的,需要各种方法配合,应该是不如其他语言方便. 我只是想看看Perl到底是否适合做这件事,于是折腾了一回. 文件的建立: 模块:Win32 use Win32; use utf8; use Encode; #接受unicode传参 Win32::CreateFile("W32CreateFile?测试"); 复制代码 特性: 成功返回true,但不返回文件句柄 Cr