如果你足够小心,就能处理它们全部。
Java的char
是一个UTF-16代码单元。对于码点大于0xFFFF的字符,将使用两个char
进行编码(即代理对)。
请参阅http://www.oracle.com/us/technologies/java/supplementary-142654.html,了解如何在Java中处理这些字符。
(顺便说一下,在Unicode 5.2中,共有1,114,112个位置中的107,154个已分配字符。)
Java使用UTF-16编码。一个Java char
只能表示基本多文种平面的字符。其他字符需要用由两个char
组成的代理对来表示。这可以通过API方法如String.codePointAt()
来反映。
是的,这意味着在处理超出基本多文种平面的字符时,很多Java代码会以某种方式出现问题。
String.length
,substring
等如何处理包含这些字符的字符串? - Bart van Heukelom除了其他回答中提到的内容外,还有一些需要记住的要点:
Java中的char类型始终占用16位。
Unicode字符在UTF-16编码时,"几乎总是"(不总是)占用16位:这是因为有超过64K个Unicode字符。因此,Java char不是Unicode字符(尽管"几乎总是"是)。
上面的"几乎总是"指的是Unicode的前64K个代码点,范围为0x0000到0xFFFF(BMP),在UTF-16编码中占用16位。
非BMP("稀有")Unicode字符表示为两个Java chars(代理表示)。这也适用于作为字符串的文字表示形式:例如,字符U+20000写为"\uD840\uDC00"。
推论:string.length()
返回的是Java chars的数量,而不是Unicode chars的数量。一个只包含一个"稀有"Unicode字符(例如U+20000)的字符串将返回length() = 2
。对于处理char序列的任何方法都适用相同的考虑。
Java对于整个非BMP Unicode字符的处理能力很弱。有一些实用程序方法将字符作为代码点处理,表示为int,例如:Character.isLetter(int ch)
。这些是真正的完全Unicode方法。
您说:
Java的char类型占用2个字节(最大大小为65,536),但Unicode字符有95,221个。
实际上,Unicode中定义的字符清单已经大幅增长。Unicode继续增长——不仅仅是因为表情符号。
char
是遗留的char
类型已经过时,现在已被遗弃。
相反,您应该使用码点数字。
你问:
这意味着Java应用程序无法处理某些Unicode字符吗?
char
类型只能表示不到今天 Unicode 字符的一半。
要表示任何 Unicode 字符,请使用代码点数字。 永远不要使用 char
。
Unicode 中的每个字符都分配了一个代码点号。 这些数字超过一百万,从0到1,114,112。 根据上面列出的数字进行计算,这意味着该范围内的大多数数字尚未分配给字符。 其中一些数字被保留为 私有使用区 ,并永远不会被分配。
String
类已经增加了处理代码点数字的方法,Character
类也是如此。
通过零基索引号获取字符串中任意字符的代码点数。在这里,我们获取字母a
的代码点数为97
。
int codePoint = "Cat".codePointAt( 1 ) ; // 97 = 'a', hex U+0061, LATIN SMALL LETTER A.
如果要使用更通用的CharSequence
而不是String
,请使用{{link1:Character.codePointAt
}}。
我们可以获取代码点号对应的Unicode名称。
String name = Character.getName( 97 ) ; // letter `a`
小写拉丁字母 A
我们可以获得一个字符串中所有字符的码位数流。
IntStream codePointsStream = "Cat".codePoints() ;
整数
对象的列表
。请参见如何将Java 8 IntStream转换为List?。List< Integer > codePointsList = codePointsStream.boxed().collect( Collectors.toList() ) ;
Character.toString
转换为单个字符的String
。String s = Character.toString( 97 ) ; // 97 is `a`, LATIN SMALL LETTER A.
我们可以从一个代码点数的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字符。当然,要看到该字符,您必须使用具有特定字符字形定义的字体。
String表示UTF-16格式的字符串,其中补充字符由代理对表示(有关更多信息,请参见Character类中的Unicode字符表示部分)。索引值是char代码单元引用,因此补充字符在String中使用两个位置。