Character与Unicode
Character 基本数据类型char 的包装类
Character 类型的对象包含一个 char 类型的字段
该类提供了几种方法来确定字符的类别(小写字母、数字等),并将字符从大写转换为小写,反之亦然
Character在 jdk8中, 基于版本Unicode6.0.2 标准
Character 类的方法和数据是通过 UnicodeData 文件中的信息定义的,
该文件是 Unicode Consortium 维护的 Unicode Character Database 的一部分
此文件指定了各种属性,其中包括每个已定义 Unicode 代码点或字符范围的名称和常规类别
此文件及其描述可从 Unicode Consortium 获得,网址如下:
http://www.unicode.org
在Java中,char 数据类型(和 Character 对象封装的值)基于原始的 Unicode 规范
将字符定义为固定宽度的 16 位实体
也就是说 char表示UTF-16编码的代码单元
对于0号平面来说,一个码点使用一个代码单元
对于辅助平面,那么一个码点将会是两个代码单元
在Unicode简介中,我们有说到,一个字符在Unicode字符集中的二进制值称为代码点
在UTF-16编码中,0号平面内,一个码点16位表示,称之为一个代码单元
总结下就是:
Unicode字符集中,一个字符对应一个代码点
UTF-16中,16位数表示的是一个代码单元
那么在0 号平面内,一个代码单元就能够表示一个代码点
但是在辅助平面,一个代码点需要两个代码单元
java中的char就是UTF-16中的代码单元
所以说,一个char表示一个代码单元,可能并不是一个字符
实在理解不了的话,就可以记住,有些字符需要两个char表示,一个char可能仅仅是某个字符的一半
Unicode字符数据库(Unicode Character Database UCD )
是由许多列出Unicode字符属性和相关数据的数据文件组成
可以查看http://www.unicode.org/ucd/ 了解UCD的相关信息
查看
https://www.unicode.org/reports/tr44/#Property_Values
中的Property Value Lists章节
General Category Values以及Bidirectional Class Values章节了解
Character中定义了大量的常量,其实就是对于这两个章节信息的描述
可以简单地理解为是字符的属性
比如一个字符可能是大写字母,可能是小写字母这样子
比如getType方法就是专门用来返回属性的,根据这个属性进而可以推断出更多的信息就像下面的的例子这样
Unicode想要深入研究,也是一门学问,此处不再继续深入,精力有限
对于Character我们只需要记住
Character 类的方法和数据是通过 UnicodeData 文件中的信息定义的 char 数据类型(和 Character 对象封装的值)基于原始的 Unicode 规范
提供的方法和数据也是基于Unicode规范来的
|
他将字符定义为固定宽度的 16 位实体,也就是只能表示一个代码单元
而Unicode也可能是有两个代码单元组成
也就是一个代码单元可能完整的表示了一个代码点,也可能是一个代码点的一部分
|
除非你真的有必要对UTF-16中的代码单元进行操作,
否则最好不要在程序中使用char类型的原因
原因很简单,一个char并不一定能够代表一个字符,可能只是一半字符
|
接下来对Character的基础方法进行介绍,过于深入Unicode的方法不再说明
有兴趣的可以研究下
常用属性
去掉上面说到的通用类别常量信息,还有以下属性
无符号二进制形式表示 char 值的位数 | public static final int SIZE = 16; |
无符号二进制形式表示 char 值的字节数 | public static final int BYTES = SIZE / Byte.SIZE; |
表示基本类型 char 的 Class 实例 | public static final Class<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char"); |
常量值是 char 类型的最小值,即 'u0000' | public static final char MIN_VALUE = 'u0000'; |
常量值是 char 类型的最大值,即 'uFFFF' | public static final char MAX_VALUE = 'uFFFF' |
Unicode代码点的最小值 | public static final int MIN_CODE_POINT = 0x000000; |
Unicode代码点的最大值 | public static final int MAX_CODE_POINT = 0X10FFFF; |
UTF-16 编码中的 Unicode 高代理项代码单元的最小值 | public static final char MIN_HIGH_SURROGATE = 'uD800'; |
UTF-16 编码中的 Unicode 高代理项代码单元的最大值 | public static final char MAX_HIGH_SURROGATE = 'uDBFF'; |
UTF-16 编码中的 Unicode 低代理项代码单元的最小值 | public static final char MIN_LOW_SURROGATE = 'uDC00' |
UTF-16 编码中的 Unicode 低代理项代码单元的最大值 | public static final char MAX_LOW_SURROGATE = 'uDFFF' |
代理项的最小值 也就是高代理项的最小值 |
public static final char MIN_SURROGATE = MIN_HIGH_SURROGATE; |
代理项的最大值 也就是低代理项的最大值 |
public static final char MAX_SURROGATE = MAX_LOW_SURROGATE; |
增补代码点的最小值 也就是除了0号平面的第一个值 |
public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000 |
可用于与字符串相互转换的最大基数 | public static final int MAX_RADIX = 36; |
可用于与字符串相互转换的最小基数 | public static final int MIN_RADIX = 2 |
可以看得出来,即使去掉了那些通用类别的常量,剩下的信息仍旧是对Unicode编码的描述
只要看过了前文的Unicode的简介,很好理解
构造方法
Character(char) | 只有一种形式的构造方法 直接设置value的值 |
常用方法
比较
compare(char, char) | 看起来可能会有人觉得奇怪,怎么还能直接减法? 其实char不就是一个UTF-16的代码单元么,他不就是一个十六进制数么 如下图所示,0x0058 - 0x002B 得到的值的十进制就是45 比较的也就是前后顺序了 |
compareTo(Character) | 实例方法 借助于静态方法 |
valueOf
系列 包装 基本类型 为 对象
XXXValue
获取 基本类型值
toString
借助于String.valueOf方法包装转换
equals
重写了equals方法,比较的是实际的对象中的value值
hashcode
直接返回value的int值
reverseBytes
public static char reverseBytes(char
ch)
character也有翻转字节转换的方法
再剩下就是Unicode更加强相关的方法了,简单介绍下
getType
获取字符在Unicode中的属性类别信息
再次强调,一个char只是一个代码单元,本质是16位 所以参数类型为char时,显然不能支持辅助平面内的字符
显然,int的范围完全足够,可以支持BMP和辅助平面
获取到的属性信息在不少针对于Unicode的处理中,将会很有用
比如
toChars
将指定的代码点,保存到char数组
一个是保存到指定数组,一个是创建一个新的数组
public
static int toChars(int codePoint, char[] dst, int dstIndex) |
保存到指定的数组的指定位置 如果0号平面内 dst[dstIndex] 中存储相同的值,并返回 1 如果辅助平面 dst[dstIndex]高代理 dst[dstIndex+1]低代理返回2 |
public
static char[] toChars(int codePoint) |
返回一个char数组保存指定代码点 |
codePointCount
返回代码点的数量,两种形式
codePointCount(CharSequence, int, int)
codePointCount(char[], int, int)
时刻记住,char是代码单元,一个代码点可能一个或者两个代码单元
offsetByCodePoints
计算偏移指定个个数的代码点后的索引
还是那句话,char是代码单元,一个代码点可能一个或者两个代码单元
如果一个代码点一个代码单元,偏移多少个,那么下标索引就是往后移动多少个了
可惜,一个代码点可能一个也可能两个代码单元
所以才有了这个方法
以第一个方法为例
就是说,给你一个数组, 然后又给了你start 和count,在这个子数组的范围内
从index这个下标开始(显然index应该位于start和start+count之间的,否则越界异常)
往后数codePointOffset 个代码点,然后看看到底下标序号是多少,返回给调用者
返回的值可能就是index+codePointOffset 也可能大于index+codePointOffset
public static int offsetByCodePoints(char[] a,
int start,
int count,
int index,
int
codePointOffset)
|
public static int offsetByCodePoints(CharSequence seq,
int index,
int
codePointOffset)
|
codePointAt
获取指定位置的代码点
基本逻辑,如果第一个代码单元的值在高代理区,下一个索引还小于数组的长度,并且下一个索引的地方是低代理区,那么返回这个代码点
否则,就是单纯的返回这个代码单元
说白了就是我能力范围内就给你,否则就给你我有的那一半 第三个limit是范围,其他的范围默认就是数组内
public static int codePointAt(char[] a, int index)
|
public static int codePointAt(CharSequence seq, int index)
|
public static int codePointAt(char[] a, int index, int limit )
|
看下下面这个例子,0x1f310上面也看到了,使用两个代码单元
chars保存了他的两个代码单元
chars1 只有一个元素,那就是0x1f310 的高代理位
codePointBefore
codePointBefore 和 codePointAt几乎是一回事情,只不过是指定位置的前面
如果指定位置前面一个 index-1 处是一个低代理,而且更前面的一个index-2
是一个数组内的有效数据
那么,就返回代码点
否则就返回一个单元
start相当于限定了数组的范围,本来index-2>=0 就好了 现在index-2需要
>=start了
public static int codePointBefore(CharSequence seq,int index)
|
public static int codePointBefore(char[] a, int index)
|
public static int codePointBefore(char[] a, int index, int start
)
|
charCount
public static int charCount(int codePoint)
确定表示指定字符(Unicode 代码点)所需的 char 值的数量。如果指定字符等于或大于
0x10000,则该方法返回的值为 2。否则,该方法返回的值为 1
从代码可以看得出来,他就是直接查看代码点的值简单的计算
并没有确认是否是有效字符
toLowerCase / toUpperCase /toTitleCase
都是使用取自 UnicodeData 文件的大小写映射信息将字符(Unicode 代码点)进行参数转换
lowCase就是转换为小写
UpperCase就是转换为大写
TitleCase就是首字母大写
又是两个版本的参数,一个char 一个int 还是老样子,char不支持辅助平面
toLowerCase(char)
toLowerCase(int)
|
toUpperCase(char)
toUpperCase(int)
|
toTitleCase(char)
toTitleCase(int)
|
代码点获取
public static char highSurrogate(int codePoint) | 返回代码点的高代理 如果不是辅助平面的字符,返回未知char |
public static char lowSurrogate(int codePoint) | 返回代码点的低代理 如果不是辅助平面的字符,返回未知char |
代理位信息判断
public static boolean isSurrogate(char ch) | 是否代理部分 |
public static boolean isSurrogatePair(char high, char low) | 是否是代理对 |
public static boolean isHighSurrogate(char ch) | 是否是高代理 |
public static boolean isLowSurrogate(char ch) | 是否是低代理 |
代码点信息的校验
public static boolean isValidCodePoint(int codePoint) | 是否是合法的代码点 确定指定的代码点是否为从 0x0000 到 0x10FFFF 范围之内的有效 Unicode 代码点值 |
public static boolean isBmpCodePoint(int codePoint) | 是否位于0号平面,是的话就可以使用一个char表示了 |
public static boolean isSupplementaryCodePoint(int codePoint) | 是否位于辅助平面 辅助平面必然需要使用两个char |
toCodePoint(char, char)
public static int toCodePoint(char high,char low)
将指定的代理项对转换为其增补代码点值。该方法没有验证指定的代理项对
如有必要,调用者必须使用 isSurrogatePair 验证它
就是高代理 低代理的合并
|
isXXX 系列
isXXX系列就是校验XXX的信息是否为真
大多两个版本,一个是char 一个是int
还是老样子,char仅仅支持0号平面
小写?
isLowerCase(char)
isLowerCase(int)
|
大写?
isUpperCase(char)
isUpperCase(int)
|
首字母大写?
isTitleCase(char)
isTitleCase(int)
|
数字?
isDigit(char)
isDigit(int)
|
被定义为 Unicode 中的字符?
isDefined(char)
isDefined(int)
|
字母?
isLetter(char)
isLetter(int)
|
字母或数字?
isLetterOrDigit(char)
isLetterOrDigit(int)
|
是否能够作为 Java 标识符中的首字符?
isJavaIdentifierStart(char)
isJavaIdentifierStart(int)
|
是否能够作为 Java 标识符中的首字符以外的字符?
isJavaIdentifierPart(char)
isJavaIdentifierPart(int)
|
是否允许作为 Unicode 标识符中的首字符?
isUnicodeIdentifierStart(char)
isUnicodeIdentifierStart(int)
|
是否允许作为 Unicode 标识符中的首字符以外的字符?
isUnicodeIdentifierPart(char)
isUnicodeIdentifierPart(int)
|
是否是 Java 标识符或 Unicode 标识符中可忽略的一个字符?
isIdentifierIgnorable(char)
isIdentifierIgnorable(int)
|
空白字符?
isSpaceChar(char)
isSpaceChar(int)
|
Java 标准是否为空白字符?
isWhitespace(char)
isWhitespace(int)
|
ISO 控制字符?
isISOControl(char)
isISOControl(int)
|
字母? isAlphabetic(int) |
中日越韩文字? isIdeographic(int) |
依据 Unicode 规范是否对称? isMirrored(char)
isMirrored(int)
|
isAlphabetic 和 isLetter 类似, 但是类型范围有差别
isAlphabetic范围更大
UPPERCASE_LETTER LOWERCASE_LETTER TITLECASE_LETTER MODIFIER_LETTER OTHER_LETTER LETTER_NUMBER
|
UPPERCASE_LETTER LOWERCASE_LETTER TITLECASE_LETTER MODIFIER_LETTER OTHER_LETTER
|
返回使用指定基数的 字符 ch/Unicode 代码点 的数值 |
public static int digit(char ch, int radix)
public static int digit(int codePoint, int radix)
|
确定使用指定基数的特定数字的字符表示形式 | public static char forDigit(int digit, int radix) |
返回给定字符的 Unicode 方向属性 |
public static byte getDirectionality(char ch)
public static byte getDirectionality(int codePoint)
|
返回指定的 Unicode 字符/Unicode 代码点 表示的int值 |
public static int getNumericValue(char ch)
public static int getNumericValue(int codePoint)
|
返回指定字符codePoint的Unicode名称,如果代码点未被分配,则返回null | public static String getName(int codePoint) |
感受下getName
总结
Java中的char 也就是Character中包装的数据
他们是UTF-16中的代码单元
一个Unicode字符集的数值叫做一个代码点
所以一个字符可能是一个char也可能是两个char
所以,除非真的有必要,希望对UTF16的代码单元进行处理,不要使用char类型,使用其他的高级类型比如String
char就是Unicode UTF-16在程序中的应用
想要理解char就必须理解Unicode和UTF16
Character中的定义的变量以及方法,大多都是和他们息息相关的