繁体   English   中英

Java Unicode 编码

[英]Java Unicode encoding

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

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

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

Java 的char是一个UTF-16 代码单元 对于代码点 > 0xFFFF 的字符,它将使用 2 个char (代理对)进行编码。

有关如何在 Java 中处理这些字符的信息,请参阅http://www.oracle.com/us/technologies/java/supplementary-142654.html

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

Java 使用UTF-16 单个 Java char只能表示来自基本多语言平面的字符。 其他字符必须由两个char代理对表示。 这反映在 API 方法中,例如String.codePointAt()

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

要添加到其他答案中,请记住以下几点:

  • Java char总是16 位

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

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

  • 非 BMP(“稀有”)Unicode 字符表示为两个 Java 字符(代理表示)。 这也适用于作为字符串的文字表示: 例如,字符 U+20000 写为“\?\?”。

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

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

你说:

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

Unicode 增长

实际上,Unicode 中定义的字符库存急剧增加。 Unicode 继续增长——不仅仅是因为表情符号

  • Unicode 13 中的 143,859 个字符(Java 15,发行说明
  • 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

使用代码点编号

相反,您应该使用代码点编号。


你问:

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

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

要表示任何 Unicode 字符,请使用代码点编号。 永远不要使用char

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

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() ;

我们可以把它变成一个Integer对象List 请参阅如何将 Java 8 IntStream 转换为列表? .

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对象。 请参阅从代码点编号的 IntStream 生成字符串? .

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 字符。 当然,要查看该字符,您必须使用带有为该特定字符定义的字形的字体。

从 Java 字符串导出文本时,如果指定的旧字符编码无法表示文本中使用的某些 Unicode 字符,则会出现问题。 所以使用现代字符编码,现在意味着UTF-8因为UTF-16 现在被认为是有害的

这是 Oracle 关于Unicode Character Representations的文档。 或者,如果您愿意,可以在此处查看更详尽的文档

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

从 U+0000 到 U+FFFF 的字符集有时称为基本多语言平面 (BMP)。 码位大于 U+FFFF 的字符称为增补字符。 Java 2 平台在 char 数组以及 String 和 StringBuffer 类中使用 UTF-16 表示。 在这种表示中,增补字符表示为一对字符值,第一个来自高代理范围 (\?-\?),第二个来自低代理范围 (\?-\?)。

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

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

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

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

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

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

来自StringOpenJDK7 文档

字符串表示 UTF-16 格式的字符串,其中补充字符由代理对表示(有关更多信息,请参阅字符类中的 Unicode 字符表示部分)。 索引值指的是字符代码单元,因此增补字符使用字符串中的两个位置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM