zoukankan      html  css  js  c++  java
  • Java编程的逻辑 (28)

    本系列文章经补充和完善,已修订整理成书《Java编程的逻辑》,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接http://item.jd.com/12299018.html


    本节探讨Character类,它的基本用法我们在包装类第一节已经介绍了,本节不再赘述。Character类除了封装了一个char外,还有什么可介绍的呢?它有很多静态方法,封装了Unicode字符级别的各种操作,是Java文本处理的基础,注意不是char级别,Unicode字符并不等同于char,本节详细介绍这些方法以及相关的Unicode知识。

    在介绍这些方法之前,我们需要回顾一下字符在Java中的表示方法,我们在第六节第七节第八节介绍过编码、Unicode、char等知识,我们先简要回顾一下。

    Unicode基础

    Unicode给世界上每个字符分配了一个编号,编号范围从0x000000到0x10FFFF。编号范围在0x0000到0xFFFF之间的字符,为常用字符集,称BMP(Basic Multilingual Plane)字符。编号范围在0x10000到0x10FFFF之间的字符叫做增补字符(supplementary character)。

    Unicode主要规定了编号,但没有规定如何把编号映射为二进制,UTF-16是一种编码方式,或者叫映射方式,它将编号映射为两个或四个字节,对BMP字符,它直接用两个字节表示,对于增补字符,使用四个字节,前两个字节叫高代理项(high surrogate),范围从0xD800到0xDBFF,后两个字节叫低代理项(low surrogate),范围从0xDC00到0xDFFF,UTF-16定义了一个公式,可以将编号与四字节表示进行相互转换。

    Java内部采用UTF-16编码,char表示一个字符,但只能表示BMP中的字符,对于增补字符,需要使用两个char表示,一个表示高代理项,一个表示低代理项。

    使用int可以表示任意一个Unicode字符,低21位表示Unicode编号,高11位设为0。整数编号在Unicode中一般称为代码点(Code Point),表示一个Unicode字符,与之相对,还有一个词代码单元(Code Unit)表示一个char。

    Character类中有很多相关静态方法,让我们来看一下。

    检查code point和char

    判断一个int是不是一个有效的代码单元:

    public static boolean isValidCodePoint(int codePoint) 

    小于等于0x10FFFF的为有效,大于的为无效。

    判断一个int是不是BMP字符:

    public static boolean isBmpCodePoint(int codePoint) 

    小于等于0xFFFF的为BMP字符,大于的不是。

    判断一个int是不是增补字符:

    public static boolean isSupplementaryCodePoint(int codePoint)

    0x010000和0X10FFFF之间的为增补字符。

    判断char是否是高代理项:

    public static boolean isHighSurrogate(char ch) 

    0xD800到0xDBFF为高代理项。

    判断char是否为低代理项:

    public static boolean isLowSurrogate(char ch) 

    0xDC00到0xDFFF为低代理项。

    判断char是否为代理项:

    public static boolean isSurrogate(char ch) 

    char为低代理项或高代理项,则返回true。

    判断两个字符high和low是否分别为高代理项和低代理项:

    public static boolean isSurrogatePair(char high, char low) 

    判断一个代码单元由几个char组成:

    public static int charCount(int codePoint) 

    增补字符返回2,BMP字符返回1。

    code point与char的转换

    除了简单的检查外,Character类中还有很多方法,进行code point与char的相互转换。

    根据高代理项high和低代理项low生成代码单元:

    public static int toCodePoint(char high, char low)

    这个转换有个公式,这个方法封装了这个公式。

    根据代码单元生成char数组,即UTF-16表示:

    public static char[] toChars(int codePoint) 

    如果code point为BMP字符,则返回的char数组长度为1,如果为增补字符,长度为2,char[0]为高代理项,char[1]为低代理项。

    将代码单元转换为char数组:

    public static int toChars(int codePoint, char[] dst, int dstIndex) 

    与上面方法类似,只是结果存入指定数组dst的指定位置index。

    对增补字符code point,生成高代理项和低代理项:

    public static char lowSurrogate(int codePoint)
    public static char highSurrogate(int codePoint) 

    按code point处理char数组或序列

    Character包含若干方法,以方便按照code point来处理char数组或序列。

    返回char数组a中从offset开始count个char包含的code point个数:

    public static int codePointCount(char[] a, int offset, int count) 

    比如说,如下代码输出为2,char个数为3,但code point为2。

    char[] chs = new char[3];
    chs[0] = '马';
    Character.toChars(0x1FFFF, chs, 1);
    System.out.println(Character.codePointCount(chs, 0, 3));

    除了接受char数组,还有一个重载的方法接受字符序列CharSequence:

    public static int codePointCount(CharSequence seq, int beginIndex, int endIndex)

    CharSequence是一个接口,它的定义如下所示:

    public interface CharSequence {
        int length();
        char charAt(int index);
        CharSequence subSequence(int start, int end);
        public String toString();
    }

    它与一个char数组是类似的,有length方法,有charAt方法根据索引获取字符,String类就实现了该接口。

    返回char数组或序列中指定索引位置的code point:

    public static int codePointAt(char[] a, int index)
    public static int codePointAt(char[] a, int index, int limit)
    public static int codePointAt(CharSequence seq, int index) 

    如果指定索引位置为高代理项,下一个位置为低代理项,则返回两项组成的code point,检查下一个位置时,下一个位置要小于limit,没传limit时,默认为a.length。

    返回char数组或序列中指定索引位置之前的code point:

    public static int codePointBefore(char[] a, int index)
    public static int codePointBefore(char[] a, int index, int start)
    public static int codePointBefore(CharSequence seq, int index)

    与codePointAt不同,codePoint是往后找,codePointBefore是往前找,如果指定位置为低代理项,且前一个位置为高代理项,则返回两项组成的code point,检查前一个位置时,前一个位置要大于等于start,没传start时,默认为0。

    根据code point偏移数计算char索引:

    public static int offsetByCodePoints(char[] a, int start, int count,
                                             int index, int codePointOffset)
    public static int offsetByCodePoints(CharSequence seq, int index,
                                             int codePointOffset)

    如果字符数组或序列中没有增补字符,返回值为index+codePointOffset,如果有增补字符,则会将codePointOffset看做code point偏移,转换为字符偏移,start和count取字符数组的子数组。

    比如,我们看如下代码:

    char[] chs = new char[3];
    Character.toChars(0x1FFFF, chs, 1);
    System.out.println(Character.offsetByCodePoints(chs, 0, 3, 1, 1));

    输出结果为3,index和codePointOffset都为1,但第二个字符为增补字符,一个code point偏移是两个char偏移,所以结果为3。

    字符属性

    我们之前说,Unicode主要是给每个字符分配了一个编号,其实,除了分配编号之外,还分配了一些属性,Character类封装了对Unicode字符属性的检查和操作,我们来看一些主要的属性。

    获取字符类型(general category):

    public static int getType(int codePoint)
    public static int getType(char ch)

    Unicode给每个字符分配了一个类型,这个类型是非常重要的,很多其他检查和操作都是基于这个类型的。

    getType方法的参数可以是int类型的code point,也可以是char类型,char只能处理BMP字符,而int可以处理所有字符,Character类中很多方法都是既可以接受int,也可以接受char,后续只列出int类型的方法。

    返回值是int,表示类型,Character类中定义了很多静态常量表示这些类型,下表列出了一些字符,type值,以及Character类中常量的名称:

    字符 type值 常量名称
    'A'
    1
    UPPERCASE_LETTER
    'a'
    2
    LOWERCASE_LETTER
    '马' 5
    OTHER_LETTER
    '1'
    9
    DECIMAL_DIGIT_NUMBER
    ' '
    12
    SPACE_SEPARATOR
    ' '
    15
    CONTROL
    '-'
    20 DASH_PUNCTUATION
    '{'
    21
    START_PUNCTUATION
    '_'
    23
    CONNECTOR_PUNCTUATION
    '&'
    24
    OTHER_PUNCTUATION
    '<'
    25
    MATH_SYMBOL
    '$'
    26
    CURRENCY_SYMBOL

     检查字符是否在Unicode中被定义:

    public static boolean isDefined(int codePoint) 

    每个被定义的字符,其getType()返回值都不为0,如果返回值为0,表示无定义。注意与isValidCodePoint的区别,后者只要数字不大于0x10FFFF都返回true。

    检查字符是否为数字:

    public static boolean isDigit(int codePoint)

    getType()返回值为DECIMAL_DIGIT_NUMBER的字符为数字,需要注意的是,不光字符'0','1',...'9'是数字,中文全角字符的0到9,即'0','1','9'也是数字。比如说:

    char ch = '9'; //中文全角数字
    System.out.println((int)ch+","+Character.isDigit(ch));

    输出为:

    65305,true

    全角字符的9,Unicode编号为65305,它也是数字。

    检查是否为字母(Letter):

    public static boolean isLetter(int codePoint)

    如果getType()的返回值为下列之一,则为Letter:

    UPPERCASE_LETTER
    LOWERCASE_LETTER
    TITLECASE_LETTER
    MODIFIER_LETTER
    OTHER_LETTER

    除了TITLECASE_LETTER和MODIFIER_LETTER,其他我们上面已经看到过了,而这两个平时碰到的也比较少,就不介绍了。

    检查是否为字母或数字

    public static boolean isLetterOrDigit(int codePoint)

    只要其中之一返回true就返回true。

    检查是否为字母(Alphabetic)

    public static boolean isAlphabetic(int codePoint)

    这也是检查是否为字母,与isLetter的区别是,isLetter返回true时,isAlphabetic也必然返回true,此外,getType()值为LETTER_NUMBER时,isAlphabetic也返回true,而isLetter返回false。Letter_NUMBER中常见的字符有罗马数字字符,如:'Ⅰ','Ⅱ','Ⅲ','Ⅳ'。

    检查是否为空格字符

    public static boolean isSpaceChar(int codePoint)

    getType()值为SPACE_SEPARATOR,LINE_SEPARATOR和PARAGRAPH_SEPARATOR时,返回true。这个方法其实并不常用,因为它只能严格匹配空格字符本身,不能匹配实际产生空格效果的字符,如tab控制键' '。

    更常用的检查空格的方法

    public static boolean isWhitespace(int codePoint) 

    ' ',' ',全角空格' ',和半角空格' '的返回值都为true。

    检查是否为小写字符

    public static boolean isLowerCase(int codePoint) 

    常见的主要就是小写英文字母a到z。

    检查是否为大写字符

    public static boolean isUpperCase(int codePoint)

    常见的主要就是大写英文字母A到Z。

    检查是否为表意象形文字

    public static boolean isIdeographic(int codePoint) 

    大部分中文都返回为true。

    检查是否为ISO 8859-1编码中的控制字符

    public static boolean isISOControl(int codePoint) 

    我们在第6节介绍过,0到31,127到159表示控制字符。

    检查是否可作为Java标示符的第一个字符

    public static boolean isJavaIdentifierStart(int codePoint) 

    Java标示符是Java中的变量名、函数名、类名等,字母(Alphabetic),美元符号($),下划线(_)可作为Java标示符的第一个字符,但数字字符不可以。

    检查是否可作为Java标示符的中间字符

    public static boolean isJavaIdentifierPart(int codePoint) 

    相比isJavaIdentifierStart,主要多了数字字符,中间可以有数字。

    检查是否为镜像(mirrowed)字符

    public static boolean isMirrored(int codePoint)

    常见镜像字符有( ) { } < > [ ],都有对应的镜像。

    字符转换

    Unicode除了规定字符属性外,对有大小写对应的字符,还规定了其对应的大小写,对有数值含义的字符,也规定了其数值。

    我们先来看大小写,Character有两个静态方法,对字符进行大小写转换:

    public static int toLowerCase(int codePoint)
    public static int toUpperCase(int codePoint)

    这两个方法主要针对英文字符a-z和A-Z, 例如:toLowerCase('A')返回'a',toUpperCase('z')返回'Z'。

    返回一个字符表示的数值:

    public static int getNumericValue(int codePoint)  

    字符'0'到'9'返回数值0到9,对于字符a到z,无论是小写字符还是大写字符,无论是普通英文还是中文全角,数值结果都是10到35,例如,如下代码的输出结果是一样的,都是10。

    System.out.println(Character.getNumericValue('A')); //全角大写A
    System.out.println(Character.getNumericValue('A'));
    System.out.println(Character.getNumericValue('a')); //全角小写a
    System.out.println(Character.getNumericValue('a'));

    返回按给定进制表示的数值:

    public static int digit(int codePoint, int radix) 

    radix表示进制,常见的有2/8/10/16进制,计算方式与getNumericValue类似,只是会检查有效性,数值需要小于radix,如果无效,返回-1,例如:

    digit('F',16)返回15,是有效的,但digit('G',16)就无效,返回-1。

    返回给定数值的字符形式

    public static char forDigit(int digit, int radix) 

    与digit(int codePoint, int radix)相比,进行相反转换,如果数字无效,返回''。例如,Character.forDigit(15, 16)返回'F'。

    与Integer类似,Character也有按字节翻转:

    public static char reverseBytes(char ch)

    例如,翻转字符0x1234:

    System.out.println(Integer.toHexString(
                    Character.reverseBytes((char)0x1234)));

    输出为3412。

    小结

    本节详细介绍了Characer类以及相关的Unicode知识,Character类在Unicode字符级别,而非char级别,封装了字符的各种操作,通过将字符处理的细节交给Character类,其他类就可以在更高的层次上处理文本了。

    至此,关于包装类我们就介绍完了。下一节,让我们在Character的基础上,进一步探索字符串类String。

    ----------------

    未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),从入门到高级,深入浅出,老马和你一起探索Java编程及计算机技术的本质。用心写作,原创文章,保留所有版权。

    -----------

    相关好评原创文章

    计算机程序的思维逻辑 (6) - 如何从乱码中恢复 (上)?

    计算机程序的思维逻辑 (7) - 如何从乱码中恢复 (下)?

    计算机程序的思维逻辑 (8) - char的真正含义

    计算机程序的思维逻辑 (26) - 剖析包装类 (上)

    计算机程序的思维逻辑 (27) - 剖析包装类 (中)

  • 相关阅读:
    版本控制
    1121 Reverse the lights(“玲珑杯”线上赛 Round #15 河南专场)
    LightOJ 1055
    LightOJ 1053
    LightOJ 1052
    4512 吉哥系列故事——完美队形I(LCIS)
    ZOJ 2432-Greatest Common Increasing Subsequence
    病毒 (湖南省第八届大学生计算机程序设计竞赛)
    1328 台球碰撞 (湖南省第六届大学生计算机程序设计竞赛)
    zzuli 1332 内部收益率 (湖南省第六届大学生计算机程序设计竞赛)
  • 原文地址:https://www.cnblogs.com/swiftma/p/5686935.html
Copyright © 2011-2022 走看看