【发布时间】:2011-02-01 18:00:03
【问题描述】:
Java char 是 2 bytes(最大大小为 65,536),但有 95,221 Unicode 字符。这是否意味着您无法在 Java 应用程序中处理某些 Unicode 字符?
这是否归结为您使用的字符编码?
【问题讨论】:
标签: java unicode character-encoding
Java char 是 2 bytes(最大大小为 65,536),但有 95,221 Unicode 字符。这是否意味着您无法在 Java 应用程序中处理某些 Unicode 字符?
这是否归结为您使用的字符编码?
【问题讨论】:
标签: java unicode character-encoding
如果你足够小心,你可以处理所有这些。
Java 的char 是UTF-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 个字符。)
【讨论】:
来自OpenJDK7 documentation for String:
一个String代表一个字符串 其中补充的UTF-16格式 字符由 代理对(参见第 Unicode 字符表示 更多的字符类 信息)。指数值参考 字符码单位,所以补充 字符在 a 中使用两个位置 字符串。
【讨论】:
Java 使用UTF-16。单个 Java char 只能表示来自 basic multilingual plane 的字符。其他字符必须由两个chars 的代理对 表示。这通过String.codePointAt()等API方法反映出来。
是的,这意味着许多 Java 代码在与基本多语言平面之外的字符一起使用时会以某种方式中断。
【讨论】:
String.length、substring等如何处理带有这些字符的字符串?
查看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 支持。
【讨论】:
要补充其他答案,请记住一些要点:
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 方法。
【讨论】:
这是 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 表意文字)。
【讨论】:
你说:
一个 Java 字符是 2 个字节(最大大小为 65,536),但有 95,221 个 Unicode 字符。
实际上,Unicode 中定义的字符清单已经急剧增加。 Unicode 继续增长——不仅仅是because of emojis。
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() ;
我们可以把它变成List 的Integer 对象。见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。
【讨论】: