如何在Java中获取字符类型的类别名称?

时间:2014-06-16 16:37:05

标签: java unicode character

Character.getType(int codePoint)返回一个整数,我无法找到方法 获取unicode类别名称,例如" Lu"或者" Cn",其中。 我想要的是一个方法,如Character.getCategoryTypeName(int codePoint),返回表示类型的字符串。

类别名称在常量注释中,和 一种方法是为返回的类型编写一个switch case,然后手动编码类型名称,如下所示:

我最初的计划是这样的:

for (int i = 0; i <= 0x10FFFF; i++) {
    switch (Character.getType(i)) {

        // General category "Sc" in the Unicode specification.
        // public static final byte CURRENCY_SYMBOL = 26;
        case Character.CURRENCY_SYMBOL: 
            map.put(i, "Sc");
            break;

        ....
     }
}
然而,这将是非常乏味的。是否有自动方式或库来完成任务?

1 个答案:

答案 0 :(得分:2)

正如所评论的,目前似乎没有与 Java 捆绑的此类功能。作为 Marcono1234 commenteda feature-request is on the books 但尚未实施。

自我注意:如果我在会议上与Brian GoetzMark Reinhold擦肩而过,请要求/恳求/恳求他们对与{{的工作进行重大改进3}} 和 code points 在 Java 中。

我想出了几种方法来为 Unicode 定义的 30 个“一般类别”项目中的每一个生成您想要的两个字母的名称。 (这个由两个字母组成的名称在 Unicode 规范中称为“别名”。)我的一个实现只是为每个别名硬编码了 switch。另一个更复杂,定义了几个枚举。

这两个实现都是我创建的,只是作为练习。我没有在生产中使用它们。我并不是说它们是最好的路线,但希望它们可能会被证明是有用的,或者至少能激励其他人做出更好的努力。

基本

在 Java 14+ 中,对 Unicode 类上定义的每个“通用类别”常量使用 switch 表达式。如果不熟悉,请参阅Character

public String unicodeGeneralCategoryAliasForCodePoint ( int codePoint ) {
    return switch ( Character.getType( codePoint ) ) {

        // L, Letter
        case Character.UPPERCASE_LETTER -> "Lu";
        case Character.LOWERCASE_LETTER -> "Ll";
        case Character.TITLECASE_LETTER -> "Lt";
        case Character.MODIFIER_LETTER -> "Lm";
        case Character.OTHER_LETTER -> "Lo";

        // M, Mark
        case Character.NON_SPACING_MARK -> "Mn";
        case Character.COMBINING_SPACING_MARK -> "Mc";
        case Character.ENCLOSING_MARK -> "Me";

        // N, Number
        case Character.DECIMAL_DIGIT_NUMBER -> "Nd";
        case Character.LETTER_NUMBER -> "Nl";
        case Character.OTHER_NUMBER -> "No";

        // P, Punctuation
        case Character.CONNECTOR_PUNCTUATION -> "Pc";
        case Character.DASH_PUNCTUATION -> "Pd";
        case Character.START_PUNCTUATION -> "Ps";
        case Character.END_PUNCTUATION -> "Pe";
        case Character.INITIAL_QUOTE_PUNCTUATION -> "Pi";
        case Character.FINAL_QUOTE_PUNCTUATION -> "Pf";
        case Character.OTHER_PUNCTUATION -> "Po";

        // S, Symbol
        case Character.MATH_SYMBOL -> "Sm";
        case Character.CURRENCY_SYMBOL -> "Sc";
        case Character.MODIFIER_SYMBOL -> "Sk";
        case Character.OTHER_SYMBOL -> "So";

        // Z, Separator
        case Character.SPACE_SEPARATOR -> "Zs";
        case Character.LINE_SEPARATOR -> "Zl";
        case Character.PARAGRAPH_SEPARATOR -> "Zp";

        // C, Other
        case Character.CONTROL -> "Cc";
        case Character.FORMAT -> "Cf";
        case Character.SURROGATE -> "Cs";
        case Character.PRIVATE_USE -> "Co";
        case Character.UNASSIGNED -> "Cn";

        default -> "ERROR - Unexpected General Category type for code point " + codePoint + ". Message # 5d44e5fd-d60e-4b02-9431-ad57c56657f5.";
    }; 
}

用法:

String alias = x.unicodeGeneralCategoryAliasForCodePoint( 65 );
<块引用>

豪华版

在这种替代方法中,我定义了一对枚举:

  • UnicodeGeneralCategory
    30 个对象,一个用于 JEP 361: Switch Expressions 的第 170-172 页中定义的每个一般类别项目。这些项目列在 section 4.5 of the Unicode 13 spec 上。
  • UnicodeMajorClass
    UnicodeGeneralCategory 的对象分组为 Unicode 规范定义的组:字母、标记、数字、标点、符号、分隔符等。最后一个“其他”值得注意,因为它涵盖了不可打印的“控制”字符以及未分配给任何字符的绝大多数代码点。当 this Wikipedia page 时,我们想跳过这些。

UnicodeGeneralCategory 中我的枚举对象的名称是从 looping over all the possible code points 类上声明的常量子集复制的,该类的描述以短语“一般类别”开头。这些常量名称与官方的 Unicode 名称有些不同,但足够接近。我按照 Unicode 规范中列出的顺序定义了这些。

package work.basil.unicode.category;

import java.util.Arrays;
import java.util.Optional;

// For more info about Unicode General Category, see section 4.5 of the Unicode 13.0 spec, pages 170-172.
// https://www.unicode.org/versions/Unicode13.0.0/ch04.pdf
public enum UnicodeGeneralCategory {

    // See Wikipedia page list the General Category values defined in Unicode 13.
    // L, Letter
    UPPERCASE_LETTER( Character.UPPERCASE_LETTER , "Lu" , "Letter" , "uppercase" ),
    LOWERCASE_LETTER( Character.LOWERCASE_LETTER , "Ll" , "Letter" , "lowercase" ),
    TITLECASE_LETTER( Character.TITLECASE_LETTER , "Lt" , "Letter" , "titlecase" ),
    MODIFIER_LETTER( Character.MODIFIER_LETTER , "Lm" , "Letter" , "modifier" ),
    OTHER_LETTER( Character.OTHER_LETTER , "Lo" , "Letter" , "other" ),

    // M, Mark
    NON_SPACING_MARK( Character.NON_SPACING_MARK , "Mn" , "Mark" , "nonspacing" ),
    COMBINING_SPACING_MARK( Character.COMBINING_SPACING_MARK , "Mc" , "Mark" , "spacing combining" ),
    ENCLOSING_MARK( Character.ENCLOSING_MARK , "Me" , "Mark" , "enclosing" ),

    // N, Number
    DECIMAL_DIGIT_NUMBER( Character.DECIMAL_DIGIT_NUMBER , "Nd" , "Number" , "decimal digit" ),
    LETTER_NUMBER( Character.LETTER_NUMBER , "Nl" , "Number" , "letter" ),
    OTHER_NUMBER( Character.OTHER_NUMBER , "No" , "Number" , "other" ),

    // P, Punctuation
    CONNECTOR_PUNCTUATION( Character.CONNECTOR_PUNCTUATION , "Pc" , "Punctuation" , "connector" ),
    DASH_PUNCTUATION( Character.DASH_PUNCTUATION , "Pd" , "Punctuation" , "dash" ),
    START_PUNCTUATION( Character.START_PUNCTUATION , "Ps" , "Punctuation" , "open" ),
    END_PUNCTUATION( Character.END_PUNCTUATION , "Pe" , "Punctuation" , "close" ),
    INITIAL_QUOTE_PUNCTUATION( Character.INITIAL_QUOTE_PUNCTUATION , "Pi" , "Punctuation" , "initial quote" ),
    FINAL_QUOTE_PUNCTUATION( Character.FINAL_QUOTE_PUNCTUATION , "Pf" , "Puntuation" , "final quote" ),
    OTHER_PUNCTUATION( Character.OTHER_PUNCTUATION , "Po" , "Punctuation" , "other" ),

    // S, Symbol
    MATH_SYMBOL( Character.MATH_SYMBOL , "Sm" , "Symbol" , "math" ),
    CURRENCY_SYMBOL( Character.CURRENCY_SYMBOL , "Sc" , "Symbol" , "currency" ),
    MODIFIER_SYMBOL( Character.MODIFIER_SYMBOL , "Sk" , "Symbol" , "modifier" ),
    OTHER_SYMBOL( Character.OTHER_SYMBOL , "So" , "Symbol" , "other" ),

    // Z, Separator
    SPACE_SEPARATOR( Character.SPACE_SEPARATOR , "Zs" , "Separator" , "space" ),
    LINE_SEPARATOR( Character.LINE_SEPARATOR , "Zl" , "Separator" , "line" ),
    PARAGRAPH_SEPARATOR( Character.PARAGRAPH_SEPARATOR , "Zp" , "Separator" , "paragraph" ),

    // C, Other
    CONTROL( Character.CONTROL , "Cc" , "Other" , "control" ),
    FORMAT( Character.FORMAT , "Cf" , "Other" , "format" ),
    SURROGATE( Character.SURROGATE , "Cs" , "Other" , "surrogate" ),
    PRIVATE_USE( Character.PRIVATE_USE , "Co" , "Other" , "private use" ),
    UNASSIGNED( Character.UNASSIGNED , "Cn" , "Other" , "not assigned" );


    // Fields.
    private byte characterClassConstantForGeneralCategory;
    private String alias, major, minor;

    // Constructor.
    UnicodeGeneralCategory ( byte characterClassConstantForGeneralCategory , String alias , String major , String minor ) {
        this.characterClassConstantForGeneralCategory = characterClassConstantForGeneralCategory;
        this.alias = alias;
        this.major = major;
        this.minor = minor;
    }

    public static UnicodeGeneralCategory forCodePoint ( int codePoint ) {
        if ( ! Character.isValidCodePoint( codePoint ) ) {
            throw new IllegalArgumentException( "Code point " + codePoint + " is invalid. Must be within 0 to U+10FFFF ( 1,114,111 ) inclusive." );
        }
        Optional < UnicodeGeneralCategory > optionalUnicodeGeneralCategory = Arrays.stream( UnicodeGeneralCategory.values() ).filter( category -> category.characterClassConstantForGeneralCategory == Character.getType( codePoint ) ).findAny();
        if ( optionalUnicodeGeneralCategory.isEmpty() ) {
            throw new IllegalStateException( "No general category defined in this enum matching `Character.getType( codePoint )`: " + Character.getType( codePoint ) );
        } else {
            return optionalUnicodeGeneralCategory.get();
        }
    }

    public static UnicodeGeneralCategory forAlias ( String abbrev ) {
        Optional < UnicodeGeneralCategory > optionalUnicodeGeneralCategory = Arrays.stream( UnicodeGeneralCategory.values() ).filter( category -> category.alias == abbrev ).findAny();
        if ( optionalUnicodeGeneralCategory.isEmpty() ) {
            throw new IllegalArgumentException( "No general category defined in this enum for abbreviation " + abbrev );
        } else {
            return optionalUnicodeGeneralCategory.get();
        }
    }

    // Getters
    public String getAlias () {
        return this.alias;
    }

    public String getMajor () {
        return this.major;
    }

    public String getMinor () {
        return this.minor;
    }

    public byte getCharacterClassConstant () {
        return this.characterClassConstantForGeneralCategory;
    }

    public String getDisplayName () {
        return this.alias + " – " + this.major + ", " + this.minor;
    }

}

……和……

package work.basil.unicode.category;

import java.util.EnumSet;
import java.util.Set;

public enum UnicodeMajorClass {
    L_Letter( "L" , "Letter" , EnumSet.of( UnicodeGeneralCategory.UPPERCASE_LETTER , UnicodeGeneralCategory.LOWERCASE_LETTER , UnicodeGeneralCategory.TITLECASE_LETTER , UnicodeGeneralCategory.MODIFIER_LETTER , UnicodeGeneralCategory.OTHER_LETTER ) ),
    M_MARK( "M" , "Mark" , EnumSet.of( UnicodeGeneralCategory.NON_SPACING_MARK , UnicodeGeneralCategory.COMBINING_SPACING_MARK , UnicodeGeneralCategory.ENCLOSING_MARK ) ),
    N_NUMBER( "N" , "Number" , EnumSet.of( UnicodeGeneralCategory.DECIMAL_DIGIT_NUMBER , UnicodeGeneralCategory.LETTER_NUMBER , UnicodeGeneralCategory.OTHER_LETTER ) ),
    P_PUNCTUATION( "P" , "Punctuation" , EnumSet.of( UnicodeGeneralCategory.CONNECTOR_PUNCTUATION , UnicodeGeneralCategory.DASH_PUNCTUATION , UnicodeGeneralCategory.START_PUNCTUATION , UnicodeGeneralCategory.END_PUNCTUATION , UnicodeGeneralCategory.INITIAL_QUOTE_PUNCTUATION , UnicodeGeneralCategory.FINAL_QUOTE_PUNCTUATION , UnicodeGeneralCategory.OTHER_PUNCTUATION ) ),
    S_SYMBOL( "S" , "Symbol" , EnumSet.of( UnicodeGeneralCategory.MATH_SYMBOL , UnicodeGeneralCategory.CURRENCY_SYMBOL , UnicodeGeneralCategory.MODIFIER_SYMBOL , UnicodeGeneralCategory.OTHER_SYMBOL ) ),
    Z_SEPARATOR( "Z" , "Separator" , EnumSet.of( UnicodeGeneralCategory.SPACE_SEPARATOR , UnicodeGeneralCategory.LINE_SEPARATOR , UnicodeGeneralCategory.PARAGRAPH_SEPARATOR ) ),
    C_OTHER( "C" , "Other" , EnumSet.of( UnicodeGeneralCategory.CONTROL , UnicodeGeneralCategory.FORMAT , UnicodeGeneralCategory.SURROGATE , UnicodeGeneralCategory.PRIVATE_USE , UnicodeGeneralCategory.UNASSIGNED ) );

    private String alias;
    private String name;
    private Set < UnicodeGeneralCategory > categories;

    UnicodeMajorClass ( String alias , String name , Set < UnicodeGeneralCategory > categories ) {
        this.alias = alias;
        this.name = name;
        this.categories = categories;
    }

    public String getAlias () {
        return alias;
    }

    public String getName () {
        return name;
    }

    public Set < UnicodeGeneralCategory > getCategories () {
        return categories;
    }

    public String getDisplayName () {
        return this.alias + " – " + this.name;
    }

    public boolean coversCodePoint ( int codePoint ) {
        return this.getCategories().contains( UnicodeGeneralCategory.forCodePoint( codePoint ) );
    }
}

用法:

  • UnicodeGeneralCategory.forCodePoint( yourCodePointGoesHere ).getAlias() 是您在问题顶部提出的要求。
  • UnicodeMajorClass.C_OTHER.coversCodePoint( codePoint ) 跳过那些讨厌的不可打印/未分配的代码点。

此外,要获取由代码点编号表示的单个字符的 String,请调用 Character.toString( codePoint )

我们可以使用这两个枚举来报告所有代码点。

package work.basil.text;

import work.basil.unicode.category.UnicodeMajorClass;

public class DumpCharacters {
    public static void main ( String[] args ) {
        System.out.println( "INFO - Demo starting. " );

        for ( int codePoint = 0 ; codePoint <= Character.MAX_CODE_POINT ; codePoint++ ) {
            if ( Character.isValidCodePoint( codePoint ) )    // If code point is valid.
            {
                if ( UnicodeMajorClass.C_OTHER.coversCodePoint( codePoint ) ) // If control character.
                {
                    // No code needed. Skip over this code point as it is not a printable character.
                } else {
                    System.out.println( codePoint + " code point is named: " + Character.getName( codePoint ) + " = " + Character.toString( codePoint ) );
                }
            } else {
                System.out.println( "ERROR - Invalid code point number: " + codePoint );
            }
        }

        System.out.println( "INFO - Demo ending. " );
    }
}

运行时:

INFO - Demo starting. 
32 code point is named: SPACE =  
33 code point is named: EXCLAMATION MARK = !
34 code point is named: QUOTATION MARK = "
35 code point is named: NUMBER SIGN = #
36 code point is named: DOLLAR SIGN = $
37 code point is named: PERCENT SIGN = %
38 code point is named: AMPERSAND = &
…
122 code point is named: LATIN SMALL LETTER Z = z
123 code point is named: LEFT CURLY BRACKET = {
124 code point is named: VERTICAL LINE = |
125 code point is named: RIGHT CURLY BRACKET = }
126 code point is named: TILDE = ~
160 code point is named: NO-BREAK SPACE =  
161 code point is named: INVERTED EXCLAMATION MARK = ¡
…
917998 code point is named: VARIATION SELECTOR-255 = ?
917999 code point is named: VARIATION SELECTOR-256 = ?
INFO - Demo ending.