Сравнивая символ с кодовой точкой?
Каков "правильный" способ сравнения кодовой точки с символом Java? Например:
int codepoint = String.codePointAt(0);
char token = '\n';
Я знаю, что могу, вероятно, сделать:
if (codepoint==(int) token)
{ ... }
но этот код выглядит хрупким. Есть ли формальный метод API для сравнения codepoints
в chars
или преобразование char
до codepoint
для сравнения?
5 ответов
Немного предыстории: когда в 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
, как вы делаете в вашем образце, работает нормально, хотя.
Класс Character содержит много полезных методов для работы с кодовыми точками Unicode. Обратите внимание на методы, такие как Character.toChars (int), которые возвращают массив символов. Если ваша кодовая точка лежит в дополнительном диапазоне, то массив будет иметь длину в два символа.
То, как вы хотите сравнить значения, зависит от того, хотите ли вы поддерживать полный диапазон значений Unicode. Этот пример кода можно использовать для итерации по кодовым точкам строки, проверяя, есть ли совпадение для дополнительного символа 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));
}
Я создал таблицу, чтобы помочь в определении длины строки Unicode и случаев сравнения, которые иногда необходимо обрабатывать.
Для символа, который может быть представлен одним символом (16 бит, базовая многоязычная плоскость), вы можете получить кодовую точку, просто приведя символ к целому числу (как предполагает вопрос), поэтому нет необходимости в специальном методе для выполнения преобразование.
Если вы сравниваете символ с кодовой точкой, вам не нужен специальный регистр. Просто сравните char с int напрямую (как предполагает вопрос). Если int представляет кодовую точку вне базовой многоязычной плоскости, результат всегда будет ложным.
Для символов в основной многоязычной плоскости приведение char к int даст вам кодовую точку. Это соответствует всем значениям Юникода, которые могут быть закодированы в одно 16-битное значение символа. Значения вне этой плоскости (с кодовыми точками, превышающими 0xffff) не могут быть выражены как один символ. Возможно, именно поэтому нет Character.toCodePoint(значение char).
Java использует 16-битную (UTF-16) модель для обработки символов, поэтому любые символы с кодовыми точками> 0xFFFF сохраняются в строках как пары 16-битных символов, используя два суррогатных символа для представления плоскости и символа в плоскости.
Если вы хотите правильно обрабатывать символы и строки в соответствии с полным стандартом Unicode, вам необходимо обрабатывать строки с учетом этого.
XML очень заботится об этом; для доступа к символьному коду полезно получить доступ к классу XMLChar в Xerces (который поставляется с Java версии 5.0 и выше).
Также полезно взглянуть на процессор Saxon XSLT / XQuery, поскольку, будучи XML-приложением с хорошим поведением, он должен учитывать, как Java хранит кодовые точки в строках. XQuery 1.0 и XPath 2.0 имеют функции для кодовых точек в строку и строки в кодовые точки; может быть поучительно взять саксонскую копию и поиграть с ними, чтобы посмотреть, как они работают.