【问题标题】:Java Unicode encodingJava Unicode 编码
【发布时间】:2011-02-01 18:00:03
【问题描述】:

Java char2 bytes(最大大小为 65,536),但有 95,221 Unicode 字符。这是否意味着您无法在 Java 应用程序中处理某些 Unicode 字符?

这是否归结为您使用的字符编码?

【问题讨论】:

    标签: java unicode character-encoding


    【解决方案1】:

    如果你足够小心,你可以处理所有这些。

    Java 的charUTF-16 code unit。对于代码点 > 0xFFFF 的字符,它将被编码为 2 chars(代理对)。

    请参阅http://www.oracle.com/us/technologies/java/supplementary-142654.html,了解如何在 Java 中处理这些字符。

    (顺便说一句,在 Unicode 5.2 中,在 1,114,112 个插槽中分配了 107,154 个字符。)

    【讨论】:

    • 上面的链接页面是我读过的最清晰的页面之一,它区分了不同的编码、JVM API 使用的内容、某些用语的含义(“代码点”与“代码单元”) ) 以及 JNI 提供的内容。
    • 下面这个网站很清楚也很详细。它甚至超出了代码点的定义,并展示了如何处理和计算字形(使用组合变音符号时可能包含多个代码点的完整渲染字符)illegalargumentexception.blogspot.jp/2009/05/…
    • 在@AllenGeorge 的评论之后,我很兴奋地阅读了这篇文章,却发现链接现在已损坏:( Ruddy Oracle 和他们无法正确 301。任何人都可以更新链接?
    • 我尝试在oracle网站上搜索并找到了这个。 http://www.oracle.com/us/technologies/java/supplementary-142654.html
    【解决方案2】:

    来自OpenJDK7 documentation for String

    一个String代表一个字符串 其中补充的UTF-16格式 字符由 代理对(参见第 Unicode 字符表示 更多的字符类 信息)。指数值参考 字符码单位,所以补充 字符在 a 中使用两个位置 字符串。

    【讨论】:

      【解决方案3】:

      Java 使用UTF-16。单个 Java char 只能表示来自 basic multilingual plane 的字符。其他字符必须由两个chars 的代理对 表示。这通过String.codePointAt()等API方法反映出来。

      是的,这意味着许多 Java 代码在与基本多语言平面之外的字符一起使用时会以某种方式中断。

      【讨论】:

      • String.lengthsubstring等如何处理带有这些字符的字符串?
      • @Bart: length() 将这样的字符计算为两个字符, substring() 也会这样做并且很乐意将它们分解,从而导致无效的 UTF-16。那是因为这些字符只是在 Java 设计出来之后才成为 Unicode 的一部分,而 Java 不做重大更改。因此,添加了新方法来处理代理对,但旧方法保持不变。
      【解决方案4】:

      查看Unicode 4.0 support in J2SE 1.5 文章,了解更多关于 Sun 发明的技巧以支持所有 Unicode 4.0 代码点。

      总之,您会发现 Java 1.5 中 Unicode 4.0 的以下更改:

      • char 是 UTF-16 代码单元,而不是代码点
      • 新的低级 API 使用 int 来表示 Unicode 代码点
      • 已更新高级 API 以了解代理对
      • 倾向于使用 char 序列 API 而不是基于 char 的方法

      由于 Java 没有 32 位字符,我会让你判断我们是否可以称之为良好的 Unicode 支持。

      【讨论】:

      • “Unicode 支持”可以通过多种方式完成,包括(但不限于)UTF-8、UTF-16 和 UTF-32 编码。在各种编码之间需要权衡取舍,但选择 UTF-16 而不是 UTF-32 并没有什么“不好”的地方。
      【解决方案5】:

      要补充其他答案,请记住一些要点:

      • Java char 始终占用 16 位

      • Unicode 字符,当编码为 UTF-16 时,“几乎总是”(不总是)16 位:这是因为有超过 64K 的 unicode 字符。因此,Java char 不是 Unicode 字符(尽管“几乎总是”是)。

      • 上面的“几乎总是”是指 Unicode 的 64K 第一个代码点,范围为 0x0000 到 0xFFFF (BMP),在 UTF-16 编码中占用 16 位。

      • 非 BMP(“稀有”)Unicode 字符表示为 两个 Java 字符(代理表示)。这也适用于作为字符串的文字表示:For example, the character U+20000 is written as "\uD840\uDC00".

      • 推论:string.length() 返回 java 字符数,而不是 Unicode 字符数。只有一个“稀有” unicode 字符(例如 U+20000)的字符串将返回 length() = 2 。同样的考虑适用于任何处理字符序列的方法。

      • Java 在处理整个非 BMP unicode 字符方面几乎没有智能。有一些实用方法将字符视为代码点,表示为整数,例如:Character.isLetter(int ch)。这些是真正的全 Unicode 方法。

      【讨论】:

      • 你不是说 0x0000 到 0xFFFF 吗?你只写 3 个 F。
      • “几乎总是”? Unicode 中超过一半的字符是用超过 64K 边界的数字定义的:Java 14 支持的 Unicode 12.1 中定义的总共 137,994 个字符。
      【解决方案6】:

      这是 Oracle 在 Unicode Character Representations 上的文档。或者,如果您愿意,可以使用 more thorough documentation here

      char 数据类型(因此是 Character 对象的值 封装)基于原始的 Unicode 规范,其中 将字符定义为固定宽度的 16 位实体。统一码 此后标准已更改为允许其 表示需要超过 16 位。法典范围 点现在是 U+0000 到 U+10FFFF,称为 Unicode 标量值。 (参考Unicode标准中U+n符号的定义。)

      从 U+0000 到 U+FFFF 的字符集有时被称为 作为基本多语言平面(BMP)。代码点的字符 大于 U+FFFF 的称为补充字符。爪哇 2 平台在 char 数组和 String 和 StringBuffer 类。在此表示中,补充 字符表示为一对 char 值,第一个来自 高代理范围,(\uD800-\uDBFF),从第二个 低代理范围 (\uDC00-\uDFFF)。

      因此,char 值表示基本多语言平面 (BMP) 代码点,包括代理代码点或代码单元 UTF-16 编码。一个 int 值代表所有 Unicode 代码点, 包括补充代码点。 较低的(最不重要的)21 int 位用于表示 Unicode 码位和上位 (最重要的)11 位必须为零。除非另有规定, 关于补充字符和代理的行为 char值如下:

      • 仅接受 char 值的方法不支持补充字符。他们处理来自代理的 char 值 范围为未定义的字符。例如, Character.isLetter('\uD840') 返回 false,即使这个特定的 如果值后跟字符串中的任何低代理值,则 代表一个字母。
      • 接受 int 值的方法支持所有 Unicode 字符,包括补充字符。例如, Character.isLetter(0x2F81A) 返回 true 因为代码点值 代表一个字母(一个 CJK 表意文字)。

      【讨论】:

        【解决方案7】:

        你说:

        一个 Java 字符是 2 个字节(最大大小为 65,536),但有 95,221 个 Unicode 字符。

        Unicode 增长

        实际上,Unicode 中定义的字符清单已经急剧增加。 Unicode 继续增长——不仅仅是because of emojis

        • Unicode 13 中的 143,859 个字符(Java 15,release notes
        • Unicode 12.1(Java 13 和 14)中的 137,994 个字符
        • Unicode 10(Java 11 和 12)中的 136,755 个字符
        • Unicode 8 (Java 9) 中的 120,737 个字符
        • Unicode 6.2 (Java 8) 中的 110,182 个字符
        • Unicode 6.0 (Java 7) 中的 109,449 个字符
        • Unicode 4.0(Java 5 和 6)中的 96,447 个字符
        • Unicode 3.0 (Java 1.4) 中的 49,259 个字符
        • Unicode 2.1 (Java 1.1.7) 中的 38,952 个字符
        • Unicode 2.0 (Java 1.1) 中的 38,950 个字符
        • Unicode 1.1.5 (Java 1.0) 中的 34,233 个字符

        char 是旧版

        char 类型早已过时,现在是 legacy

        使用代码点编号

        相反,您应该使用code point 号码。


        你问:

        这是否意味着您无法在 Java 应用程序中处理某些 Unicode 字符?

        char 类型可以处理不到当今 Unicode 字符的一半。

        要表示any Unicode character,请使用code point 数字。 切勿使用char

        Unicode 中的每个字符都分配有一个代码点编号。这些范围超过一百万,从 0 到 1,114,112。与上面列出的数字进行比较时进行数学运算,这意味着该范围内的大多数数字尚未分配给字符。其中一些号码保留为Private Use Areas,永远不会被分配。

        String 类获得了处理代码点编号的方法,Character 类也是如此。

        通过从零开始的索引号获取字符串中任何字符的代码点号。在这里,我们得到 97 的字母 a

        int codePoint = "Cat".codePointAt( 1 ) ; // 97 = 'a', hex U+0061, LATIN SMALL LETTER A.
        

        对于更通用的CharSequence 而不是String,请使用Character.codePointAt

        我们可以获取代码点编号的 Unicode 名称。

        String name = Character.getName( 97 ) ; // letter `a`
        

        拉丁文小写字母 A

        我们可以得到一个字符串中所有字符的码位编号流。

        IntStream codePointsStream = "Cat".codePoints() ;
        

        我们可以把它变成ListInteger 对象。见How do I convert a Java 8 IntStream to a List?

        List< Integer > codePointsList = codePointsStream.boxed().collect( Collectors.toList() ) ;
        

        通过调用Character.toString,可以将任何代码点编号更改为单个字符的String

        String s = Character.toString( 97 ) ; // 97 is `a`, LATIN SMALL LETTER A. 
        

        一个

        我们可以从代码点编号的IntStream 生成String 对象。见Make a string from an IntStream of code point numbers?

        IntStream intStream = IntStream.of( 67 , 97 , 116 , 32 , 128_008 ); // 32 = SPACE, 128,008 = CAT (emoji).
        
        String output =
                intStream
                        .collect(                                     // Collect the results of processing each code point.
                                StringBuilder :: new ,                // Supplier<R> supplier
                                StringBuilder :: appendCodePoint ,    // ObjIntConsumer<R> accumulator
                                StringBuilder :: append               // BiConsumer<R,​R> combiner
                        )                                             // Returns a `CharSequence` object.
                        .toString();                                  // If you would rather have a `String` than `CharSequence`, call `toString`. 
        

        猫?


        你问:

        这归结为您使用的是什么字符编码?

        在内部,Java 中的String 始终使用UTF-16

        在将文本导入或导出 Java 字符串时,您只能使用其他字符编码。

        所以,回答你的问题,不,字符编码在这里没有直接关系。将文本转换为 Java String 后,它采用 UTF-16 编码,因此可以包含任何 Unicode 字符。当然,要看到该字符,您必须使用为该特定字符定义了glyph 的字体。

        从 Java 字符串中导出文本时,如果您指定的旧版 character encoding 不能表示文本中使用的某些 Unicode 字符,则会出现问题。所以使用现代字符编码,现在将UTF-8 表示为UTF-16 is now considered harmful

        【讨论】:

          猜你喜欢
          • 2014-08-08
          • 2019-02-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-19
          • 2012-04-10
          • 1970-01-01
          相关资源
          最近更新 更多