我看到评论here,charAt
的所有解决方案都是错误的。我无法完全理解并在互联网上找到关于charAt
的内容。当我查看源代码时,它只返回char数组中的一个元素。所以我的问题是,如果使用charAt
有任何问题或问题吗?
评论就是那样
严格地说,基于charAt的所有解决方案都是错误的 charAt不会给你“the at at”,而是“code unit at”, 并且有代码单元不是字符和字符 需要多个代码单元。
答案 0 :(得分:14)
使用不同的字节数编码不同的字符(使用UTF-16方案)。例如,“A”字符表示如下:
01000001
到目前为止一切顺利。
但如果你有像这样的角色,你就会遇到问题。其UTF-16表示(BE)是:
11011000 00110101 11011101 00110100
然后charAt
确实可以返回该角色的第二个代码单元。
参见String#charAt
的JDK 7实现:
public char charAt(int index) {
if ((index < 0) || (index >= count)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index + offset];
}
答案 1 :(得分:11)
在Java中,String
本质上是char
的数组。同样,char
是UCS-2(UTF-16)代码点。
这有两个问题:
重新排序属于这两种情况之一的字符会导致String
不正确。
StringBuilder
's reverse考虑了第一种情况,但我不知道任何考虑到第二种情况的内容。
答案 2 :(得分:6)
上面说的是真的,一些代码单元需要表示两个字符。由于Java使用16位字符,因此很少遇到;但严格来说,任何使用charAt(...)而不考虑被访问的char是否是两个char代码单元的一部分的代码都会暴露给字符处理问题。
要测试您是否使用两个字符代码单元,您应该检查charAt(...)
的初始值是否在0xD800
到0xDFFF
的范围内;因为该范围表示两个字符代码单元的开始。
答案 3 :(得分:6)
正如其他答案所指出的那样,某些字符可能会占用多个代码单元,如果您尝试单独解释这些代码单元中的任何一个,或者与其他代码单元结合使用,您将获得无效字符。
要记住的另一件事是,在字符串中使用2代码单元字符会将所有后续索引移动1,例如第十个字符将是charAt(10)
而不是charAt(9)
- 因此,即使您没有遇到字符本身的编码问题,您也会发现自己在字符串中稍后通过索引提取错误的字符。 / p>
答案 4 :(得分:5)
严格来说,是的,存在问题,正如您强调的原因所述。问题是某些角色可能需要多于1 char
来表示。因此,通过使用String.charAt,当你反转字符串时,你会有一个新的半随机字符,因为按照构成该字符的两个字符的顺序切换。
但同样,这是严格来说
答案 5 :(得分:5)
关于文本有许多常见的致命破坏假设,特别是如果你离开“只有一个西方国家”的利基,你在使用unicode时就会这样做。
只是在处理UTF-16时专门开始一些相关的要点:
反转文本时的其他相关性是LTR和RTL覆盖,需要特殊处理。
我建议您阅读Why does modern Perl avoid UTF-8 by default?的已接受答案,特别是假设破解部分,该部分与编程语言无关。
答案 6 :(得分:3)
String.charAt
方法 是安全的(对于某些“安全”的定义),但如果您的字符串包含Basic Multilingual Plane以外的字符,则可以使用它,而不安全代码点的范围为0到65535。
您可以使用String.charAt
实现字符串撤消 - AbstractStringBuilder
直接使用char[]
,但这在逻辑上与使用String.charAt()
相同。它基本上实现了两个过程:
答案 7 :(得分:2)
您问题最简单的例子是UTF-8字符,例如ñ..
charAt()将轻松返回ASCII字符,因为ASCII字符占用1个字节。另一方面,UTF-8 / UTF-16字符可能占用多个字节,因此您可能会收到意外的输出。
许多语言都有UTF-8格式的字母/符号,所以假设您的应用程序提供了某些特定于语言环境的信息,您可能会使用utf-8字符,而charAt()在这种情况下会失败。