有效的Unicode字符串可以包含FFFF吗?Java/CharacterIterator坏了吗?

pol*_*nts 24 java string unicode

以下是java.text.CharacterIterator文档的摘录:

  • interface定义了一个用于文本双向迭代的协议.迭代器迭代有界字符序列.[...]方法previous()next()用于迭代.它们返回DONE[...],表示迭代器已到达序列的末尾.

  • static final char DONE:迭代器到达文本的结尾或开头时返回的常量.值是\uFFFF,不是任何有效的Unicode字符串中不应出现的"非字符"值.

斜体部分是我无法理解的部分,因为从我的测试来看,它看起来像Java String当然可以包含\uFFFF,并且似乎没有任何问题,除了明显的规定的CharacterIterator遍历习惯因为打破因为假阳性(例如,当它没有真正"完成"时next()返回'\uFFFF' == DONE).

这里有一个片段来说明"问题"(另见ideone.com):

import java.text.*;
public class CharacterIteratorTest {

    // this is the prescribed traversal idiom from the documentation
    public static void traverseForward(CharacterIterator iter) {
       for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
          System.out.print(c);
       }
    }

    public static void main(String[] args) {
        String s = "abc\uFFFFdef";

        System.out.println(s);
        // abc?def

        System.out.println(s.indexOf('\uFFFF'));
        // 3

        traverseForward(new StringCharacterIterator(s));
        // abc
    }
}
Run Code Online (Sandbox Code Playgroud)

那么这里发生了什么?

  • 规定的遍历成语是否"被打破",因为它做出了错误的假设\uFFFF
  • StringCharacterIterator实现"破",因为它并不如throw一个IllegalArgumentException如果事实上\uFFFF在有效Unicode字符串是被禁止的?
  • 实际上有效的Unicode字符串是否应该包含\uFFFF
  • 如果这是真的,那么Java是否因为违反Unicode规范而被"破坏"(对于大多数部分而言)允许String包含\uFFFF

Joe*_*oey 27

EDIT(2013-12-17): Peter O.在下面提出了一个很好的观点,这使得这个答案错了​​.以下旧答案,历史准确性.


回答你的问题:

规定的遍历成语是否"被破坏",因为它对\ uFFFF做出了错误的假设?

不,U + FFFF是所谓的非角色.从Unicode标准的第16.7节:

非字符是Unicode标准中永久保留供内部使用的代码点.它们被禁止用于Unicode文本数据的开放式交换.

...

Unicode标准留出了66个非字符代码点.每个平面的最后两个代码点都是非字符:BMP上的U + FFFE和U + FFFF,平面1上的U + 1FFFE和U + 1FFFF,依此类推,直到平面16上的U + 10FFFE和U + 10FFFF,共有34个代码点.此外,BMP中还有另外32个非字符代码点的连续范围:U + FDD0..U + FDEF.

StringCharacterIterator实现是否"已损坏",因为它实际上不会抛出IllegalArgumentException,如果事实上在有效的Unicode字符串中禁止使用\ uFFFF?

不完全的.允许应用程序以他们想要的任何方式在内部使用这些代码点.再次引用标准:

应用程序可以在内部自由使用任何这些非字符代码点,但绝不应 尝试交换它们.如果在开放式交换中收到非字符,则不需要应用程序以任何方式解释它.但是,将它识别为非字符并采取适当的操作(例如用U + FFFD REPLACEMENT CHARACTER替换它)来表示文本中的问题是一种很好的做法.由于删除未解释的字符可能导致安全问题,因此建议不要简单地从此类文本中删除非字符代码点.

因此,虽然您永远不会遇到来自用户,其他应用程序或文件的字符串,但如果您知道自己在做什么,则可以将其放入Java字符串中(这基本上意味着您无法在该字符串上使用CharacterIterator,虽然.

实际上有效的Unicode字符串不应该包含\ uFFFF吗?

如上所述,用于交换的任何字符串都不得包含它们.在您的应用程序中,您可以以任何他们想要的方式自由使用它们.

当然,Java char只是一个16位无符号整数,并不真正关心它所拥有的值.

如果这是真的,那么Java是否因为(对于大多数部分)违反Unicode规范而"破坏",从而允许String包含\ uFFFF?

实际上,非字符部分甚至建议使用U + FFFF作为哨兵值:

实际上,非字符可以被认为是应用程序内部的私有代码点.与第16.5节中讨论的私人使用字符不同,私人使用字符是指定字符并且打算用于公开交换,受私人协议解释,非字符永久保留(未分配),并且在外部没有任何解释他们可能的应用程序 - 内部私人用途.

U + FFFF和U + 10FFFF.这两个非特征代码点具有与特定Unicode编码形式的最大代码单元值相关联的属性.在UTF-16中,U + FFFF与最大的16位代码单元值FFFF 16相关联.U + 10FFFF与最大的合法UTF-32 32位代码单元值10FFFF 16相关联.此属性将这两个非字符代码点用作内部目的作为标记.例如,它们可能用于指示列表的结尾,表示保证高于任何有效字符值的索引中的值,依此类推.

CharacterIterator遵循这一点,因为当没有更多字符可用时它返回U + FFFF.当然,这意味着如果您在应用程序中对该代码点有另一种用途,您可以考虑为此目的使用不同的非字符,因为已经采用了U + FFFF - 至少如果您使用的是CharacterIterator.


Pet*_* O. 19

其中一些答案在此期间发生了变化.

Unicode联盟最近发布了勘误表9,阐明了非字符的作用,包括U + FFFF,在Unicode字符串中的作用.它指出虽然非字符用于内部使用,但它们可以合法地出现在Unicode字符串中.

这意味着声明"值是\ uFFFF,'不是字符'值,不应出现在任何有效的Unicode字符串中." 现在是不正确的,因为U + FFFF 可以出现在有效的Unicode字符串中.

因此:

  • "遍历成语"是否被打破?是的,因为它对Unicode字符串中U + FFFF的有效性做出了错误的假设.
  • StringCharacterIterator实现是否"已损坏",因为如果在有效的Unicode字符串中禁止\ uFFFF,它不会抛出异常?由于U + FFFF有效,因此不适用于此处.但是,当一个实现遇到由于其他原因而非法的文本时,例如未配对的代理代码点仍然是非法的(参见Unicode标准第3章中的符合性条款C10),实现具有很大的灵活性.
  • 有效的Unicode字符串是否应该包含\ uFFFF?U + FFFF在有效的Unicode字符串中不是非法的.但是U + FFFF保留为非字符,因此通常不会出现在有意义的文本中.更正删除了非字符"永远不应该互换"的文本,更正说"任何时候Unicode字符串跨越API边界",包括此处讨论的StringCharacterIterator API.
  • 如果这是真的,那么Java是否因为允许String包含\ uFFFF而违反Unicode规范而"破解"?java.lang.String"A String表示UTF-16格式的字符串" 的规范.U + FFFF在Unicode字符串中是合法的,因此Java在包含它的字符串中允许U + FFFF时不违反Unicode.