Подтвердить что ты не робот

Может ли действующая строка Unicode содержать FFFF? Является ли Java/CharacterIterator нарушенным?

Здесь выдержка из 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
    }
}

Итак, что здесь происходит?

  • Является ли предписанная обходная идиома "сломанной", потому что она делает неправильное предположение о \uFFFF?
  • Является ли реализация StringCharacterIterator "сломанной", потому что она не является, например, throw a IllegalArgumentException, если на самом деле \uFFFF запрещено в действительных строках Unicode?
  • Действительно ли верно, что действующие строки Unicode не должны содержать \uFFFF?
  • Если это правда, тогда Java "сломан" для нарушения спецификации Unicode (для большинства частей), позволяя String содержать \uFFFF в любом случае?
4b9b3361

Ответ 1

EDIT (2013-12-17): Peter O. поднимает превосходный момент ниже, что делает этот ответ неправильным. Старый ответ ниже, для исторической точности.


Отвечая на ваши вопросы:

Является ли предписанная обходная идиома "сломанной", потому что она делает неправильное предположение о \uFFFF?

Нет. U + FFFF является так называемым несимвольным. Из Раздел 16.7 стандарта Unicode:

Нехарактеры - это кодовые точки, которые постоянно сохраняются в стандарте Unicode для внутреннего использования. Они запрещены для использования при открытом обмене текстовыми данными Юникода.

...

В стандарте Unicode выделяются 66 нехарактерных кодовых точек. Последние два кодовых пункта каждая плоскость нехарактера: U + FFFE и U + FFFF на BMP, U + 1FFFE и U + 1FFFF на плоскости 1 и т.д., до U + 10FFFE и U + 10FFFF на плоскости 16, в общей сложности 34 кода точки. Кроме того, существует смежный диапазон еще 32 нехарактерных кодовых точек в BMP: U + FDD0..U + FDEF.

Является ли реализация StringCharacterIterator "сломанной", потому что она не является, например, бросить исключение IllegalArgumentException, если на самом деле \uFFFF запрещен в действительных строках Unicode?

Не совсем. Приложениям разрешено использовать эти кодовые точки внутри себя любым способом. Повторное цитирование стандарта:

Приложения могут свободно использовать любую из этих нехарактерных кодовых точек внутри, но должны никогда не пытайтесь их обменивать. Если в открытом обмене получен нехарактер, приложение не обязано каким-либо образом интерпретировать его. Однако хорошей практикой является признать ее нехарактерным и предпринять соответствующие действия, например, заменить ее на U + FFFD REPLACEMENT CHARACTER, чтобы указать на проблему в тексте. Не рекомендуется просто удалите нехарактерные коды из такого текста из-за потенциальной безопасности проблемы, вызванные удалением неинтерпретируемых символов.

Таким образом, хотя вы никогда не должны сталкиваться с такой строкой от пользователя, другого приложения или файла, вы можете поместить его в строку Java, если вы знаете, что делаете (это в основном означает, что вы не можете использовать CharacterIterator on эта строка, однако.

Действительно ли верно, что действительные строки 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 - связанный с наибольшим юридическим значением 32-битного кода UTF-32, 10FFFF 16. Этот атрибут делает эти два нехарактерных кодовых пункта полезными для внутренних целей как часовые. Для Например, они могут использоваться для указания конца списка, для представления значения в индексе гарантированно выше любого действительного значения символа и т.д.

CharacterIterator следует за тем, что он возвращает U + FFFF, когда больше символов не доступно. Конечно, это означает, что если у вас есть другое использование для этой кодовой точки в вашем приложении, вы можете использовать другой несимвольный для этой цели, так как U + FFFF уже занят - по крайней мере, если вы используете CharacterIterator.

Ответ 2

Некоторые из этих ответов изменились за это время.

Консорциум Unicode недавно выпустил Исправление 9, в котором разъясняется роль нехарактеров, включая U + FFFF, в строках Unicode. В нем указано, что, хотя нехарактеры предназначены для внутреннее использование, они могут встречаться юридически в строках Unicode.

Это означает утверждение "Значение:\uFFFF, значение" не является символом ", которое не должно происходить ни в одной из допустимых строк Unicode". сейчас неверно, так как U + FFFF может встречаться в действительных строках Unicode.

Соответственно:

  • Является ли "обходная идиома" нарушенной? Да, потому что она делает неправильное предположение о достоверности U + FFFF в строках Unicode.
  • Является ли реализация StringCharacterIterator "сломанной", потому что она не генерирует исключение, если \uFFFF запрещается в действительных строках Unicode? Так как U + FFFF действителен, здесь это не применимо. Но реализация имеет широкую гибкость в сигнализации об ошибке, когда она встречается текст, незаконный по другим причинам, например непарные суррогатные кодовые пункты, которые все еще остаются незаконными (см. условие соответствия C10 в главе 3 стандарта Unicode).
  • Верно ли, что допустимые строки Unicode не должны содержать \uFFFF? U + FFFF не является незаконным в действительной строке Unicode. Однако U + FFFF зарезервирован как нехарактерный, и, как правило, в значимом тексте не возникает. Исправление удалил текст, что несимволы "никогда не должны меняться", что, по словам корректора, происходит "всякий раз, когда строка Unicode пересекает границу API", в том числе API StringCharacterIterator, проблема здесь.
  • Если это правда, тогда Java "сломан" для нарушения спецификации Unicode позволяя String содержать \uFFFF в любом случае? Спецификация для java.lang.String говорит: "A String представляет строка в формате UTF-16. "U + FFFF является законным в строке Unicode, поэтому Java не нарушает Unicode для разрешения U + FFFF в строке, содержащей его.

Ответ 3

Является ли реализация StringCharacterIterator "сломанной", потому что она не является, например, бросить исключение IllegalArgumentException, если на самом деле \uFFFF запрещен в действительных строках Unicode?

Не строго в соответствии с Unicode, но он несовместим с остальными интерфейсами обработки строк Java, и эта несогласованность может иметь очень неприятные эффекты. Подумайте о всех дырах в безопасности, которые мы получили от строковой обработки, которые делают против, и не рассматривают \0 как терминатор.

Я бы сильно избегал интерфейса CharacterIterator.

Ответ 4

Да, CharacterIterator использует 0xFFFF, поскольку значение DONE является немного аномальным. Но все это имеет смысл с точки зрения эффективной обработки текста.

Класс String не запрещает 0xFFFF "несимвольные" и другие зарезервированные или неотображаемые коды Unicode. Для этого потребуется, чтобы конструкторы String проверяли каждое заданное значение char. Также были бы проблемы с обработкой текста, содержащего кодовые точки Юникода, определенные в будущем (по JVM) версии Unicode.

С другой стороны, интерфейс CharacterIterator предназначен для обеспечения итерации путем вызова одного только метода; т.е. next(). Они решили использовать выделенное значение char для обозначения "не более", потому что другие альтернативы:

  • выброс исключения (что слишком дорого) или
  • используя int в качестве возвращаемого типа, жизнь которого более сложна для вызывающего.

Если CharacterIterator используется для "реального" текста Unicode, то тот факт, что вы не можете включить 0xFFFF, не является проблемой. Действительный текст Юникода не содержит эту кодовую точку. (Фактически, причина, по которой 0xFFFF зарезервирована как несимвольная, заключается в поддержке приложений, где текст Unicode представлен как строки, заканчивающиеся несимвольным значением. Использование 0xFFFF в качестве символа полностью нарушило бы.)

В нижней строке:

  • Если вы хотите строгие строки Unicode, не используйте String и
  • если вы хотите перебирать строки Java, содержащие значения 0xFFFF, тогда не используйте CharacterIterator.