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

时间:2010-08-14 09:03:22

标签: java string unicode

以下摘自java.text.CharacterIterator文档:

  
      
  • interface定义了对文本进行双向迭代的协议。迭代器迭代有界字符序列。 [...]方法previous()next()用于迭代。如果[...]则返回DONE,表示迭代器已到达序列的末尾。

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

  •   

斜体部分是我无法理解的部分,因为从我的测试来看,它看起来像Java String肯定包含\uFFFF,并且似乎没有任何问题有了它,除了明显有规定的CharacterIterator遍历习语由于误报而中断(例如next()当它没有真正“完成”时返回'\uFFFF' == DONE。)

这是一个片段来说明“问题”(see also on 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
    }
}

那么这里发生了什么?

  • 规定的遍历成语是否“被破坏”,因为它对\uFFFF做出了错误的假设?
  • StringCharacterIterator实施是否“已损坏”,因为它没有例如如果实际throw在有效的Unicode字符串中被禁止,则IllegalArgumentException \uFFFF
  • 有效的Unicode字符串是否真的不应包含\uFFFF
  • 如果这是真的,那么Java是否因为(大多数部分)违反Unicode规范而“破解”,允许String包含\uFFFF

4 个答案:

答案 0 :(得分:27)

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


回答你的问题:

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

没有。 U + FFFF是所谓的非角色。来自Section 16.7 of the Unicode Standard

  

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

     

...

     

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

StringCharacterIterator实现是否“损坏”,因为它没有例如如果事实上在有效的Unicode字符串中禁止使用\ uFFFF,则抛出IllegalArgumentException?

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

  

应用程序可以在内部自由使用任何这些非字符代码点,但应该   从不尝试交换它们。如果在开放式交换中收到非特征,则a   申请不需要以任何方式解释。但是,将它识别为非字符并采取适当的操作(例如用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。

答案 1 :(得分:18)

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

Unicode联盟最近发布了Corrigendum 9,澄清了这一角色 Unicode字符串中的非字符,包括U + FFFF。它指出虽然非人物是为了 在内部使用,他们可以在Unicode字符串中合法发生。

这意味着语句“值为\ uFFFF,'不是字符'值,不应出现在任何有效的Unicode字符串中。”就是现在 不正确,因为U + FFFF 可以出现在有效的Unicode字符串中。

因此:

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

答案 2 :(得分:3)

  

StringCharacterIterator实现是否“已损坏”,因为它没有例如如果实际上在有效的Unicode字符串中禁止使用\ uFFFF,则抛出IllegalArgumentException?

不严格按照Unicode,但它与Java的其他字符串处理接口不一致,并且这种不一致可能会产生非常不愉快的影响。想想我们从字符串处理中获得的所有安全漏洞,而不是将\0视为终结符。

我会强烈避免使用CharacterIterator界面。

答案 3 :(得分:2)

是的,CharacterIterator使用0xFFFF作为DONE值有点异常。但从有效的文本处理角度来看,这一切都是有道理的。

String类不禁止0xFFFF“非字符”以及其他保留或未映射的Unicode代码点。为此,需要String构造函数检查每个提供的char值。它还会出现处理包含将来(相对于JVM)Unicode版本定义的Unicode代码点的文本的问题。

另一方面,CharacterIterator接口旨在通过调用一个just方法来允许迭代;即next()。他们决定使用一个显着的char值来表示“不再”,因为其他选择是:

  • 抛出异常(太贵)或
  • 使用int作为返回类型,对于调用者来说生活更复杂。

如果CharacterIterator用于“真正的”Unicode文本,那么您不能包含0xFFFF的事实不是问题。有效的Unicode文本不包含此代码点。 (实际上,将0xFFFF保留为非字符的原因是支持将Unicode文本表示为以非字符值终止的字符串的应用程序。使用0xFFFF作为字符会完全破坏该字符。)

底线是:

  • 如果您需要严格的Unicode字符串,请不要使用String
  • 如果要迭代包含0xFFFF值的Java字符串,则不要使用CharacterIterator。