将char与代码点进行比较?

时间:2009-06-22 23:25:28

标签: java unicode

将代码点与Java角色进行比较的“正确”方法是什么?例如:

int codepoint = String.codePointAt(0);
char token = '\n';

我知道我可能会这样做:

if (codepoint==(int) token)
{ ... }

但这段代码看起来很脆弱。是否有正式的API方法可用于将codepointschars进行比较,或将char转换为codepoint进行比较?

5 个答案:

答案 0 :(得分:43)

一点背景:当Java出现在1995年时,char类型基于原始的“Unicode 88”规范,该规范限制为16位。一年后,当实施Unicode 2.0时,引入了代理字符的概念超出了16位的限制。

Java内部代表UTF-16格式的所有String。对于超过U + FFFF的代码点,代码点由代理对表示,即两个char s,第一个是高代理代码单元,(在范围内\ uD800- \ uDBFF),第二个作为低代理代码单元(在\ uDC00- \ uDFFF范围内)。

从早期开始,所有基本的Character方法都基于一个代码点可以用char表示的假设,这就是方法签名的样子。我想保留当Unicode 2.0出现时没有改变的向后兼容性,并且在处理它们时需要谨慎。引用Java documentation

  • 仅接受char值的方法不支持补充字符。它们将代理范围中的char值视为未定义的字符。例如,Character.isLetter('\ uD840')返回false,即使后面跟着字符串中任何低代理值的特定值也代表一个字母。
  • 接受int值的方法支持所有Unicode字符,包括补充字符。例如,Character.isLetter(0x2F81A)返回true,因为代码点值表示一个字母(CJK表意文字)。

char投射到int,就像在示例中一样,但工作正常。

答案 1 :(得分:10)

Character类包含许多用于处理Unicode代码点的有用方法。注意像Character.toChars(int)这样的方法返回一组字符。如果您的代码点位于补充范围内,那么该数组的长度将为两个字符。

您希望如何比较这些值取决于您是否要支持所有Unicode值。此示例代码可用于迭代String的代码点,测试是否匹配补充字符MATHEMATICAL_FRAKTUR_CAPITAL_G(? - U + 1D50A):

public final class CodePointIterator {

  private final String sequence;
  private int index = 0;

  public CodePointIterator(String sequence) {
    this.sequence = sequence;
  }

  public boolean hasNext() {
    return index < sequence.length();
  }

  public int next() {
    int codePoint = sequence.codePointAt(index);
    index += Character.charCount(codePoint);
    return codePoint;
  }

  public static void main(String[] args) {
    String sample = "A" + "\uD835\uDD0A" + "B" + "C";
    int match = 0x1D50A;
    CodePointIterator pointIterator = new CodePointIterator(sample);
    while (pointIterator.hasNext()) {
      System.out.println(match == pointIterator.next());
    }
  }
}

对于Java 8及以上版本CharSequence.codePoints()可以使用:

public static void main(String[] args) {
  String sample = "A" + "\uD835\uDD0A" + "B" + "C";
  int match = 0x1D50A;
  sample.codePoints()
        .forEach(cp -> System.out.println(cp == match));
}

我创建了一个table来帮助处理有时需要处理的Unicode字符串长度和比较情况。

答案 2 :(得分:3)

对于可以用单个char(16位,基本多语言平面)表示的字符,只需将char转换为整数(如问题所示)就可以得到代码点,因此不需要特殊的执行转换的方法。

如果您要将char与代码点进行比较,则不需要任何特殊的外壳。只需将char与int直接比较(如问题所示)。如果int表示基本多语言平面之外的代码点,则结果将始终为false。

答案 3 :(得分:2)

对于基本多语言平面中的字符,将char转换为int将获得代码点。这对应于可以在单个16位char值中编码的所有unicode值。此平面外的值(代码点超过0xffff)不能表示为单个字符。这可能就是没有Character.toCodePoint(char值)的原因。

答案 4 :(得分:0)

Java使用16位(UTF-16)模型来处理字符,因此任何带有代码点&gt;的字符都是如此。 0xFFFF作为16位字符的存储在字符串中,使用两个surrogate字符来表示平面内的平面和字符。

如果要根据完整的Unicode标准正确处理字符和字符串,则需要处理考虑到这一点的字符串。

XML非常关心这一点;对于与字符相关的代码,访问Xerces中的XMLChar类(Java版本5.0及更高版本)非常有用。

查看Saxon XSLT / XQuery处理器也很有启发性,因为它是一个表现良好的XML应用程序,它必须考虑Java如何在字符串中存储代码点。 XQuery 1.0和XPath 2.0具有codepoints-to-stringstring-to-codepoints的功能;获得撒克逊的副本并与他们一起玩,看看它们是如何工作的可能是有益的。