Сравнивая символ с кодовой точкой? - PullRequest
33 голосов
/ 23 июня 2009

Каков «правильный» способ сравнения кодовой точки с символом Java? Например:

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

Я знаю, что, вероятно, могу сделать:

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

но этот код выглядит хрупким. Существует ли формальный метод API для сравнения codepoints с chars или преобразования char до codepoint для сравнения?

Ответы [ 5 ]

43 голосов
/ 23 июня 2009

Немного предыстории: когда в 1995 году появилась Java, тип char был основан на исходной спецификации " Unicode 88 ", которая была ограничена 16 битами. Год спустя, когда был внедрен Unicode 2.0, была введена концепция суррогатных символов, выходящая за пределы 16-битного ограничения.

Java внутренне представляет все String с в формате UTF-16. Для кодовых точек, превышающих U + FFFF, кодовая точка представлена ​​суррогатной парой, т. Е. Двумя char s, причем первая - это высокосуррогатная кодовая единица (в диапазоне \ uD800- \ uDBFF), вторая - код с низким суррогатным кодом (в диапазоне \ uDC00- \ uDFFF).

С самых первых дней все основные Character методы основывались на предположении, что кодовая точка может быть представлена ​​в одном char, так что именно так выглядят сигнатуры методов. Я предполагаю сохранить обратную совместимость, которая не изменилась, когда появился Unicode 2.0, и при работе с ними необходима осторожность. Цитировать из документации Java :

  • Методы, которые принимают только значение символа, не могут поддерживать дополнительные символы. Они обрабатывают значения символов из суррогатных диапазонов как неопределенные символы. Например, Character.isLetter ('\ uD840') возвращает false, даже если это конкретное значение, если за ним следует любое низкосуррогатное значение в строке, будет представлять букву.
  • Методы, принимающие значение типа int, поддерживают все символы Unicode, включая дополнительные символы. Например, Character.isLetter (0x2F81A) возвращает true, поскольку значение кодовой точки представляет букву (идеограф CJK).

Преобразование char в int, как в примере, работает нормально.

10 голосов
/ 23 июня 2009

Класс Character содержит много полезных методов для работы с кодовыми точками Unicode. Обратите внимание на такие методы, как Character.toChars (int) , которые возвращают массив символов. Если ваша кодовая точка находится в дополнительном диапазоне, то массив будет иметь длину в два символа.

Способ сравнения значений зависит от того, хотите ли вы поддерживать полный диапазон значений Unicode. Этот пример кода можно использовать для итерации по кодовым точкам строки, проверяя, есть ли совпадение для дополнительного символа MATHEMATICAL & # x005F; FRAKTUR & # x005F; CAPITAL & # x005F; G (& # x1D50A; - 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));
}

Я создал таблицу , чтобы помочь в определении длины строки Unicode и случаев сравнения, которые иногда необходимо обрабатывать.

3 голосов
/ 23 июня 2009

Для символа, который может быть представлен одним символом (16 бит, базовая многоязычная плоскость), вы можете получить кодовую точку, просто приведя символ к целому числу (как предполагает вопрос), поэтому нет необходимости способ выполнить преобразование.

Если вы сравниваете символ с кодовой точкой, вам не нужен специальный регистр. Просто сравните char с int напрямую (как предполагает вопрос). Если int представляет кодовую точку вне базовой многоязычной плоскости, результат всегда будет ложным.

2 голосов
/ 23 июня 2009

Для символов в основной многоязычной плоскости приведение char к int даст вам кодовую точку. Это соответствует всем значениям Юникода, которые могут быть закодированы в одно 16-битное значение символа. Значения вне этой плоскости (с кодовыми точками, превышающими 0xffff) не могут быть выражены как один символ. Возможно, именно поэтому нет Character.toCodePoint (значение char).

0 голосов
/ 23 июня 2009

Java использует 16-битную (UTF-16) модель для обработки символов, поэтому любые символы с кодовыми точками> 0xFFFF сохраняются в строках как пары из 16-битных символов с использованием двух суррогатов символов для представления плоскости и символа в плоскости.

Если вы хотите правильно обрабатывать символы и строки в соответствии с полным стандартом Unicode, вам необходимо обрабатывать строки с учетом этого.

XML очень заботится об этом; для кода, связанного с символами, полезно получить доступ к классу XMLChar в Xerces (который поставляется с Java версии 5.0 и выше).

Также поучительно взглянуть на процессор XSLT / XQuery Saxon , поскольку, будучи XML-приложением с хорошим поведением, он должен учитывать, как Java хранит кодовые точки в строках. XQuery 1.0 и XPath 2.0 имеют функции для кодовых точек в строку и строк в кодовые точки ; может быть поучительно взять копию саксонского и поиграть с ними, чтобы посмотреть, как они работают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...