使用charAt方法进行字符串反转时可能出现的问题

use*_*111 15 java string charat

我在这里看到一条评论说所有解决方案charAt都是错误的.我无法完全理解并charAt在互联网上找到一些东西.当我查看源代码时,它只返回char数组中的一个元素.所以我的问题是,如果有任何问题或使用问题charAt

评论就是这样

严格来说,所有基于charAt的解决方案都是错误的,因为charAt不会给你"字符",而是"代码单位",而且代码单元不是需要多个代码单元的字符和字符.

Mar*_*oun 14

不同的字符用不同的字节数编码(使用UTF-16方案).例如,"A"字符表示如下:

01000001
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

但如果你有一个喜欢的角色 ,你会遇到问题.它的UTF-16表示(BE)是:

11011000 00110101 11011101 00110100
Run Code Online (Sandbox Code Playgroud)

然后charAt确实可以返回该角色的第二个代码单元.

请参阅JDK 7实现String#charAt:

public char charAt(int index) {
    if ((index < 0) || (index >= count)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index + offset];
}
Run Code Online (Sandbox Code Playgroud)


Pow*_*ord 11

在Java中,String本质上是一个数组char.同样,a char是UCS-2(UTF-16)代码点.

这有两个问题:

  1. 并非所有字符都可以用UTF-16中的单个代码点表示.
  2. Unicode支持组合字符.

重新排序属于这两种情况之一的字符将导致String不正确.

StringBuilder反过来考虑了第一种情况,但我不知道任何考虑到第二种情况的事情.


Edw*_*uck 6

上面说的是真的,一些代码单元需要表示两个字符.由于Java使用16位字符,因此很少遇到; 但严格来说,任何使用charAt(...)而不考虑被访问的char是否是两个char代码单元的一部分的代码都会暴露给字符处理问题.

为了测试,如果您使用的是两个char代码单元工作时,应检查是否从初始值charAt(...)的范围是从0xD8000xDFFF; 因为该范围表示两个字符代码单元的开始.


Cup*_*Tae 6

正如其他答案所指出的,某些字符可以占用多个代码单元,如果您尝试单独解释这些代码单元中的任何一个,或者与其他代码单元组合,您将获得无效字符.

另外要记住的是,在字符串中使用2个代码单元字符会将所有后续索引移动一个,因此例如第十个字符将charAt(10)代替charAt(9)- 所以即使您没有受到编码问题的影响对于角色本身,您可能会发现自己后来在字符串中通过索引提取错误的字符.


Con*_*Del 5

严格地说,是的,存在问题,正如您强调的原因所述.问题是某些字符可能需要多于1 char才能表示.因此,通过使用String.charAt,当你反转字符串时,你将有一个新的半随机字符,因为按照构成该字符的两个字符的顺序切换.

但同样,这是严格来说


Ded*_*tor 5

关于文本有许多常见的致命破坏假设,特别是如果你离开"只是一个西方国家"的利基,你在使用unicode时就会这样做.
只是在处理UTF-16时专门提出一些相关要点:

  • 代码点可能是多个代码单元.
  • 字符可能是多个代码点.
  • 代码点可能是多个字符.

反转文本时的另外相关性是LTR和RTL覆盖,这需要特殊处理.

我建议你阅读接受的答案为什么现代Perl默认避免使用UTF-8?,特别是该部分假定破碎,该部分是编程语言不可知的.