好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

unicode编码在Android中的应用

一.unicode编码

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。

编码范围:0x0000-0x10FFFF。

此范围被分成了17个平面,其中,平面0(0x0000-0xffff)是基本多文种平面,包含了英文字母,阿拉伯数字,以及时间常用的各国语言。平面1(0x10000-0x1ffff)是多文种补充平面,包含了各国语言的补充以及emoji等。平面2(0x20000-0x2ffff)表意文字补充平面,此整个平面专为CJK即中日韩汉字编码设计。平面14(0xe0000-0xeffff)特别用途补充平面,此平面目前编码很少。平面15和平面16(0xf0000-10ffff)私人使用区,即自定义编码平面。其它平面基本上未编码。这个平面划分在维基百科上一目了然,下面附有链接地址。

平面0的专用区和代理区是未编码的具体如下:

专用区:0xE000-0xF8FF,6400个码位

专用区是留给自定义编码用的。

代理区:0xD800-0xDFFF,2048个码位

代理区是为utf-16编码方案用的,下面会具体讲到。

emoji:1F600-1F64F,2600-26FF,...

汉字:4e00-9FFF,扩展区A:3400-4DBF,扩展区B-F:20000-2EBEF

例如:汉字‘润'-0x6da6,emoji‘’-0x1f606

二.unicode编码实现

UTF:Unicode Transformation Format,即Unicode字符集转换格式。Unicode有3种实现方式即:utf-8、 utf-16、utf-32

1、utf-8:

以8比特位为一个单位,一个字符的表示最少一个单位即一个字节,最大4个单位即4个字节。具体用几个单位需要看字符在unicode字符集里的码位,具体参考下表。

Unicode编码(十六进制) UTF-8 字节流(二进制) 000000-00007F 0xxxxxxx 000080-0007FF 110xxxxx 10xxxxxx 000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx 010000-10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 2、utf-16:

以16比特位为一个单位,一个字符的表示要么用一个单位即2个字节,要么用2个单位即4个字节。Unicode编码小于0x10000就用一个单位表示,0x10000-0x10ffff就用2个单位表示。

a.U<0x10000,utf-16就对应编码本身的无符号整数

b. U>=0x10000,

7字口诀就是:一减,二转,三模板。

1).减:U'=U-0x10000

例:0x1f606-0x1000

2).转:把U'转换为20位二进制,yyyy yyyy yyxx xxxx xxxx

3).模板:1101 10yy yyyy yyyy 1101 11xx xxxx xxxx

可以算一下,1101 10yy yyyy yyyy的取值范围1101 1000  0000 0000 - 1101 1011 1111 1111即D800-DBFF, 1101 11xx xxxx xxxx的取值范围1101 1100 0000 0000 - 1101 1111 1111 1111

即DC00-DFFF。所以,D800-DFFF即是unicode专为utf-16设计的代理区,其中,D800-DBFF是高代理区,DC00-DFFF是低代理区。代理区共2048个码位,unicode设计为保留区,没有具体编码,只为代理用。

我们在上面了解到平面15和平面16(0xf0000-0x10ffff)是私人使用区,我们把这个区间的编码使用上面公式可以算出,0xf0000-0x10000=e0000即11100000000000000000‬,放入模板,1101 1011 1000 0000(DB80) 1101 1100 0000 0000(DC00),0x10ffff-0x10000=0xfffff,即11111111111111111111‬,放入模板,1101 1011 1111 1111(DBFF) 1101 1111 1111 1111(DFFF)。因为平面15和16专用区utf-16的高位代理实现范围是:DB80-DBFF,所以DB80-DBFF(D800-DBFF高位代理区的子区间)又称为高位专用代理区。

相反,utf-16编码求unicode

a.判断:非代理区,此字符对应U<0x10000

b.代理区

1).模板:1101 10yy yyyy yy yy 1101 11xx xxxx xxxx

2).转:把yyyy yyyy yyxx xxxx xxxx转换为十六进制U'

3).加:U=U'+0x10000

3、utf-32

以32个比特位为一个单位,即4个字节。所有的unicode编码都可以直接表示了,而且码位远远用不完。实现很简单,但是由于大部分字符用两个字节足够,4个字节太浪费空间了,所以很少用。

4、utf-8和utf-16方案对比

1000汉字,1000英文对比

新建一个文本文件,随便输入1000个常见(4e00-9FFF)汉字,发现有3KB的大小。因为windows记事本默认用utf-8编码,而一个汉字用utf-8表示需要用3个字节,所以1000字就大约是3KB。我们文本另存为utf-16格式,发现大小变为了2KB。这因为utf-16表示汉字只需要2个字节,1000个汉字大约2KB。如果把1000汉字换成1000个英文字母或阿拉伯数字,结果你会发现,用utf-8格式只有1KB,而utf-16仍然是2KB。这是因为,对于1个英文字母或1个阿拉伯数字utf-8用一个单位即1个字节就可以表示了,而utf-16也至少要用一个单位就是2个字节。这个对比可以帮助我们直观的理解记忆utf-8和utf-16的区别。

也正因为全世界大部分常用字符码位还是在0x100-0xffff这个范围,所以最佳方案还是utf-16。Java char的设计就是一个很好的说明。java char 2个字节,默认编码方式utf-16。当然如果是纯英文和阿拉伯数字的程序选用utf-8编码也是可以理解的。

5、字节序

把文本文档另存为utf-16时发现,确切的格式是UTF-16LE或者UTF-16BE。

BE(Big Endian)大字节字节序、LE(Little Endian)小字节字节序

注意:字节的顺序,非比特位的顺序;单位内字节顺序,非全部字节顺序。仔细观察对比0x6da6和0x1f606这两个字符的utf-16和utf-32实现,下表例子,你会明白这句话意思。

Unicode编码 UTF-16LE UTF-16BE UTF32-LE UTF32-BE 0x6da6 a6 6d 6d a6 a6 6d 00 00 00 00 6d a6 0x1f606 3D D8 06 DE D8 3D DE 06 06 f6 01 00 00 01 f6 06

utf-8一个单位一个字节,所以没有大小字节序的区分。

UTF编码 Byte Order Mark (BOM) UTF-8 without BOM 无 UTF-8 with BOM EF BB BF UTF-16LE FF FE UTF-16BE FE FF UTF-32LE FF FE 00 00 UTF-32BE 00 00 FE FF

注:FEFF-ZWNBSP:Zero-Width No-Break Space零宽不断行空白

微软在自己的UTF-8格式的文本文件之前加上了EF BB BF三个字节,

windows上面的notepad等程序就是根据这三个字节来确定一个文本文件是ASCII的还是UTF-8的,

然而这个只是微软暗自作的标记, 其它平台上不一定会对UTF-8文本文件做这样的标记。

微软的一些软件会做这种检测,但有些软件不做这种检测, 而把它当作正常字符处理。(传说中的乱码问题)

三.实战,判断字符串里是否包含emoji

1、需求

只要输入的内容包含空格、花椒表情、emoji表情或超过10个字符就不关联,否则字符个数变动就关联。

需求分解:输入的内容是否包含emoji

2、实现
 public static boolean containsEmoji(String str) {

    int len = str.length();

    for (int i = 0; i < len; i++) {

        int codePoint = Character.codePointAt(str, i);

        if (isEmojiCharacterByWiki(codePoint)) {

            return true;

        }

    }

    return false;

}
 
 /**

 * 是否是emoji编码

 * //Superscripts and Subscripts( 2070 - 209F )上标及下标

 * //Currency Symbols( 20A0 - 20CF )外汇符号

 * //Combining Diacritical Marks for Symbols( 20D0 - 20FF )

 * //Letterlike Symbols( 2100 - 214F )字母式符号 ℃ 、™

 * //Number Forms( 2150 - 218F )数字形式   ⅓ Ⅷ

 * //Arrows( 2190 - 21FF )箭头   →

 * //Mathematical Operators( 2200 - 22FF )数学操作符

 * //Miscellaneous Technical( 2300 - 23FF )零杂技术用符号

 * //Control Pictures( 2400 - 243F )控制图片

 * //Optical Character Recognition( 2440 - 245F )光学字符辨识

 * //Enclosed Alphanumerics( 2460 - 24FF )封闭式字母数字

 * //Box Drawing( 2500 - 257F )

 * //Box Elements( 2580 - 259F )

 * //Geometric Shapes( 21A0 - 21FF )几何图形

 * //Miscellaneous Symbols( 2600 - 26FF )杂项符号

 * //Dingbats ( 2700 - 27BF )装饰标志

 * //Miscellaneous Mathematical Symbols-A ( 27C0 - 27EF )杂项数学符号

 * //Supplemental Arrows-A ( 27F0 - 27FF )追加箭头

 * //Braille Patterns ( 2800 - 28FF )盲文点字模型

 * //Supplemental Arrows-B ( 2900 - 297F )追加箭头

 * //Miscellaneous Mathematical Symbols-B( 2980 - 29FF )杂项数学符号

 * //Supplemental Mathematical Operators( 2A00 - 2AFF )追加数学操作符

 * //Miscellaneous Symbols and Arrows( 2B00 - 2BFF )杂项符号和箭头

 * 

 * //CJK Symbols and Punctuation( 3000 - 30FF )CJK符号和标点

 * 

 * //Enclosed CJK Letters and Months( 3000 - 30FF )CJK封闭式字符和月份

 * 

 * // Mahjong Tiles( 1F000 - 1F02F )麻将牌

 * // Domino Tiles( 1F030 - 1F09F )多米诺骨牌

 * // Playing Cards( 1F0A0 - 1F0FF )扑克牌

 * //Enclosed Alphanumeric Supplement( 1F100 - 1F1FF )封闭式字母数字补充

 * //Enclosed Ideographic Supplement ( 1F200 - 1F2FF )封闭式表意文字补充

 * //Miscellaneous Symbols and Pictographs ( 1F300 - 1F5FF )其他符号和象形文字

 * //Emoticons ( 1F600 - 1F64F )

 * //Ornamental Dingbats ( 1F650 - 1F67F )

 * //Transport and map symbols ( 1F680 - 1F6FF )

 * //Alchemical Symbols ( 1F700 - 1F77F )炼金术的符号

 * //Geometric Shapes Extended ( 1F780 - 1F7FF )几何图形扩展

 * //Supplemental Arrows-C ( 1F800 - 1F8FF )追加箭头-C

 * //Supplemental Symbols and Pictographs ( 1F900 - 1F9FF )补充符号和象形文字

 * //Chess Symbols ( 1FA00 - 1FA6F )国际象棋的符号

 * https://en.wikibooks.org/wiki/Unicode/Character_reference

 *

 * @param codePoint

 * @return

 */

private static boolean isEmojiCharacterByWiki(int codePoint) {

    return ((codePoint >= 0X2070) && (codePoint <= 0X2BFF)) ||

            ((codePoint >= 0X3000) && (codePoint <= 0X30FF)) ||

            ((codePoint >= 0X3200) && (codePoint <= 0X32FF)) ||

            ((codePoint >= 0x1F000) && (codePoint <= 0x1FA6F));

}
 

参考:

1.When the specification for the Java language was created, the Unicode standard was accepted and the char primitive was defined as a 16-bit data type, with characters in the hexadecimal range from 0x0000 to 0xFFFF.

Java语言标准创建的时候,就采纳了Unicode标准,char原始类型被定义为16-bit数据类型,能存储的字符范围用16进制表示是从0x0000到0xffff。

https://docs.oracle.com/javase/tutorial/i18n/text/unicode.html

2.The native character encoding of the Java programming language is UTF-16.

https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html

3.维基百科对unicode编码的编排

https://zh.wikibooks.org/wiki/Unicode/%E5%AD%97%E7%AC%A6%E5%8F%82%E8%80%83/1F000-1FFFF

4.emoji编码分类

https://apps.timwhitlock.info/emoji/tables/unicode

查看更多关于unicode编码在Android中的应用的详细内容...

  阅读:91次