пятница, 13 мая 2011 г.

У каждого символа есть история №4: U+feff

Это перевод Every character has a story #4: U+feff (alternate title: UTF-8 is the BOM, dude!). Автор: Майкл Каплан.

U+feff работает на двух работах в стандарте Unicode.

Работа №1, и её имя - ZERO WIDTH NO-BREAK SPACE. Ну, её имя всё говорит и так. В конце-концов, у нас есть U+00a0 (NO-BREAK SPACE) и U+200b (ZERO WIDTH SPACE). Хотя нам надо как-то скомбинировать их, чтобы создать символ, у которого нулевая ширина, но при этом вы не должны разрывать строку между тем, что слева от него и справа. Позже было решено ввести для этого отдельный символ (U+200d, ZERO WIDTH JOINER). В итоге Unicode-приложение не должно делать с U+feff ничего особенного, потому что у этого символа нулевая длина. Так что если вы полностью его проигнорируете, то получите идентичные результаты, как если бы его вовсе не было.

Обновление: TLKH указал, что символом, который взял на себя работу №1 после увольнения U+feff, был U+2060 (WORD JOINER), а не U+200d (ZERO WIDTH JOINER).

Работа №2 - это работа в качестве BOM (Byte Order Mark). Это сигнатура в начале файла для указания кодировки текста в нём - чтобы кто-то мог посмотреть на поток байтов и узнать по последовательности байтов, что кодировка в файле может быть:
  00 00 fe ff      UTF-32, Big Endian
  fe ff 00 00      UTF-32, Little Endian
  fe ff            UTF-16, Big Endian
  ff fe            UTF-16, Little Endian
  ef bb bf         UTF-8
И вот эта последняя строка смущает многих людей. Потому что куча людей, которые поддерживают UTF-8 в других стандартах (вроде XML), замечают, что вам не нужен BOM, потому что у вас есть другие способы задания кодировки документа, и которые (стандарты) при этом используют UTF-8 как вариант по-умолчанию, если кодировка вовсе не указана. И куче других людей, которые поддерживают утилиты Unix и не полностью переключились на поддержку Unicode с использованием UTF-8, не нравятся эти дополнительные три байта в начале каждого файла. Иногда это потому, что они на самом деле работают только с ASCII или ISO-8859-1, иногда это потому, что у них просто нет возможности обработать эти три байта в начале текста.

На сцену выходит Microsoft.

(Да, я знаю: крики "фу", помидоры и т.п.)

У Microsoft есть приложение, называемое Блокнот (Notepad). Приложение - это, наверное, слишком сильно сказано. Это просто убер-оболочка вокруг элемента управления Win32 EDIT. Он хранит текст в EDIT-е - что означает, что если вы открываете файл, то Блокнот буквально загружает все его данные, чтобы запихнуть их в элемент управления (вот почему открытие больших файлов длится вечность). С течением времени в Блокнот были добавлены мелкие возможности и улучшения. Но центральный смысл остался неизменным - это не более, чем EDIT контрол в окне.

Когда Блокнот был портирован на NT, то ему добавили опцию сохранения Unicode, потому что в NT была поддержка Unicode, так что её добавили. Изначально это была поддержка Little Endian UTF-16 (это было единственное, что поддерживали платформы тех дней), но это было названо просто "Unicode", потому что в то время про этого зверя никто вообще не слышал. Кроме того, именно это поддерживали все остальные и других вариантов на практике просто не было.

Позже в Windows 2000 была добавлена возможность сохранять файл в Big Endian UTF-16, а поскольку Little Endian UTF-16 уже был назван "Unicode", то было решено назвать "этот другой формат" как "Unicode (Big Endian)". Я не думаю, что это так уж плохо. Это, конечно, менее спорно, чем название unicodeFFFE1, хотя текущий способ наименования раздражает людей, которые не хотят, чтобы один какой-то формат назывался бы "Unicode": как будто остальные форматы - это не Unicode.

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

Но был совершён ещё более тяжкий грех - в Блокнот добавили поддержку UTF-8. И, конечно же, тут и вылезла проблема с BOM.

Ребята из команды Оболочки (Shell), которые реализовывали эту поддержку, увидели, что если в файле есть только ASCII-символы, то он может быть назван UTF-8-файлом, либо же файлом в текущей ANSI кодировке. Так что если пользователь сохранил файл в UTF-8, а потом открыл его и не увидел UTF-8 (файл был ошибочно принят за ANSI) - то это было бы весьма запутывающе, сохранять файл без BOM. Поэтому, они добавляют BOM к файлу, когда он сохраняется в UTF-8 - чтобы пометить файл как UTF-8, что на 100% согласуется с Unicode.

Это полностью легально и, поскольку Блокнот представляет собой простую обёртку вокруг EDIT-а, у него нет других способов воспринимать информацию, чтобы узнать кодировку. Что ещё могла сделать команда Оболочки? Баг же состоит в том, что люди используют Блокнот для правки HTML, XML и других вещей, которые не требуют BOM, а указывают кодировку иными способами. Люди всё ещё используют для этих целей Блокнот, хотя подводные камни этого вполне очевидны...

Люди вроде Реймонда Чена говорили про то, что некоторые файлы странно показываются в Блокноте, но обычно люди не жалуются на это поведение Блокнота.

Но каждые 4-6 месяцев создаётся очередное огромное обсуждение в списках рассылки Unicode о том, как плохо использование BOM для UTF-8 и как оно ломает UNIX-утилиты, которые поддерживают UTF-8 без малейшего изменений десятилетиями2, и что Microsoft - корпорация зла, потому что её Блокнот вызывает все эти проблемы, и что ни W3C, ни Unicode никогда бы не поддерживали BOM для UTF-8, если бы Microsoft не включила это в Блокнот, и так далее, и тому подобное.

Верьте или нет, в очередной ветке обсуждения, открытой только что, уже набежало 30+ сообщений.

Почему-то никто не говорит про то, что если кто-то действительно поддерживает Unicode, то обработать ZERO WIDTH NO-BREAK SPACE для него должно быть раз плюнуть. Если они это сделать не могут, то это баг в их приложении (или что там они пишут), а не вина Microsoft. По крайней мере, если они декларируют поддержку UTF-8. Утилиты, которые поддерживают "всё в UTF-8, но только если он начинается с ASCII", и утилиты, которые не могут обработать эти три байта, на самом деле просто не поддерживают UTF-8.

И, кстати, сюда же относятся и приложения Microsoft. На мой взгляд, FrontPage 2000 немного пованивает (учитывая все обстоятельства) из-за этой проблемы. Даже хотя они добавили крутую опцию "не трогай мои HTML параметры", которую я так люблю. Я был очень счастлив, когда FrontPage 2002 и 2003 исправили эту проблемы. Точно так же, как, я уверен, большинство других людей были бы счастливы, если бы авторы других утилит исправили бы свои творения...

Я думаю, что стоит кратко процитировать один из постов из списка рассылок Unicode, который был только что сделан Peter Constable:
Насчёт того, должны ли простые текстовые файлы иметь BOM или нет - это один из бесконечных споров, которые поднимаются постоянно (к счастью, не очень часто). И каждый раз - с громогласным выражением глубокого убеждения, но никогда не с решением. Я только замечу, что формальная грамматика XML не делает ссылок на BOM, хотя XML спецификация, безусловно, предполагает, что правильно сформированный XML-документ может начинаться с UTF-8 BOM (или любого другого BOM). Вместо того, чтобы вести бесконечную философскую дискуссию об определении "текстовый файл", я предлагаю более прагматичный подход: хорошо это или плохо, но редакторы текста с поддержкой UTF-8 будут встречать данные UTF-8, начинающиеся с BOM: так что просто научитесь жить с этим!
Я на 100% согласен с его словами, и мне хотелось бы уметь так же ясно и эффективно подводить итоги, как это делает он :-)

И для галочки: в прошлом опция сохранения файлов в UTF-8 без BOM не была такой уж плохой идеей. Конечно же, это означает документирование этого подхода для других людей, которые будут это использовать.

1 - Верьте или нет, но unicodeFFFE на самом деле документировано в Internet Explorer как Preferred Charset Label для Unicode (Big-Endian). Иногда люди сообщают это как баг, потому что в Unicode нет кодовой точки U+fffe. Но причина для такого имени состоит в том, что если вы посмотрите на BOM кодировки UTF-16 big endian на системе с порядком little endian, то BOM будет выглядеть как FFFE. Поскольку это недопустимый символ, то очевидно, что у нас текст в кодировке с обратным порядком байт.

2 - Ничего, что даже Unicode тогда не существовал, не говоря уже про UTF-8!

This post is sponsored by "" U+feff (ZERO WIDTH NO-BREAK SPACE, конечно же)
Хотя он был немного расстроен отсутствием своего визуального представления, я не смог найти никого, кто бы нарисовал его для всех вас. Он находится между этими кавычками - я гарантирую это.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.