среда, 24 ноября 2010 г.

"Майкл, ну почему ToTitleCase такой отстой?"

Это перевод "Michael, why does ToTitleCase suck so much?". Автор: Майкл Каплан.

В заголовке этого поста я, на самом деле, просто процитировал e-mail, который я получил по этой теме. E-mail, так похожий на другие, которые я получаю время от времени с тех пор, как я начал публиковать заметки об особенностях изменения регистра строки. И это ещё одно из самых невинных писем!

Кажется, люди просто ненавидят класс TextInfo из-за его метода ToTitleCase.

Я процитирую версию этого вопроса "поприятнее", которую отправил кто-то, назвавшийся Ruben, в мой Suggestion Box:
Возможно, тебе стоит написать статью про проблемы использования ToTitleCase, которая находится в состоянии войны с почти любым стилем и просто обожает акронимы (хотя я думаю, что та любовь не взаимна), а также портит произношение в таких языках как немецкий и Gaelic (к примеру: IJmuiden и Oileán na gCapall, оба из которых являются полностью корректными формами с заглавной буквой в начале в соответствующих языках). Это будет иллюстрация, почему Unicode не решает лингвистические проблемы, хотя многие люди предполагают противное.
Вы можете увидеть, почему я заключил слово "поприятнее" в кавычки, поскольку многие могут подумать как и я: что не надо использовать смягчающие выражения для публикации текста, который резок и неприятен!

Для начала нам надо завести свою машину времени, чтобы посмотреть на старую функцию VB/VBA StrConv и её опцию vbProperCase. Эта функция "конвертирует первую букву каждого слова в строке в верхний регистр". Она делает это, определяя символы разделения слов:
Следующие символы являются допустимыми разделителями слов: Null (Chr$(0)), horizontal tab (Chr$(9)), linefeed (Chr$(10)), vertical tab (Chr$(11)), form feed (Chr$(12)), carriage return (Chr$(13)), space (SBCS) (Chr$(32)). Настоящее значение для space для DBCS-строк зависит от страны.
Заметьте, что эта функция демонстрирует тот же уровень игнорирования национальных особенностей, даже хотя у неё есть параметр LCID. В реальности же он мало на что влияет.

Эта VB функция становится немного лучше в VB.Net (StrConv в VB.Net): теперь она учитывает лингвистическое изменение регистра, что здорово для турецкого...

А потом у нас появляется стандарт Unicode, который определяет заголовочный регистр (title case) следующим образом:
Из-за включения определённых составных (composite) символов ради обратной совместимости (таких как U+01F1 "DZ" LATIN CAPITAL LETTER DZ) появляется третий регистр (кроме верхнего и нижнего), называемый titlecase, который используется, когда первый символ слова должен быть заглавным. Пример такого символа: U+01F2 "Dz" LATIN CAPITAL LETTER D WITH SMALL LETTER Z.

Выбор слов для titlecase зависит от языка. К примеру, "Taming of the Shrew" будет корректной записью в английском, а "Taming Of The Shrew" - нет. Более того, определение того, что вообще понимать под словом, также зависит от языка. К примеру, l'arbre может рассматриваться как два слова во Франции, но это не является одним словом в английском.

В большинстве случаев, titlecase - это аналог uppercase, но не всегда. К примеру, titlecase от U+01F1 "DZ" capital dz будет U+01F2 "Dz" capital d with small z.

Также есть слова вроде vederLa в итальянском, имени McGowan в английском, которые не являются словом ни в верхнем, ни в нижнем, ни в заголовочном регистре. Этот формат иногда называется innerCaps и он часто используется в программировании и Web. Как только строку "McGowan" приведут к верхнему, нижнему или заголовочному регистру, исходное слово не может быть получено этими же операциями. Кроме того, существуют также отдельные символы, которые не имеют обратного преобразования, вроде греческой буквы сигма.
Unicode решительно намекает на сложности заголовочного регистра в языках, но не слишком-то помогает в решении практических проблем и поддержки такого преобразования (задача, которая, очевидно, потребует для своего решения словари, правила языков и наборы данных. Это даже делает из неё отличный вопрос для проведения интервью :-)

Хотя этот текст говорит о многих вещах, но реально данные для заголовочного регистра в Unicode ограничены несколькими кодовыми точками вроде этой самой DZ (U+01f1, LATIN CAPITAL LETTER DZ), которая волшебным образом становится Dz (U+01f2, LATIN CAPITAL LETTER D WITH SMALL LETTER Z.

С другой стороны, давайте посмотрим на метод ToTitleCase класса TextInfo, который описывает себя так:
В общем случае заголовочный регистр переводит первую букву каждого слова к верхнему регистру, а остальные буквы слова - к нижнему.

Возвращаемая строка может отличаться длиной от входной строки. Для дальнейшей информации по регистровым операциям см. Unicode Technical Report #21 "Case Mappings", опубликованный Unicode Consortium (http://www.unicode.org/). Текущая реализация сохраняет длину строки; однако, это поведение не гарантировано и может измениться в будущем.

Семантика изменения регистра зависит от выбранной культуры (в .NET это аналог локали из native - прим.пер.). Если используется инвариантная культура, то семантика изменения регистра не чувствительна к культуре. Если используется конкретная культура, то семантика изменения регистра будет зависеть от этой культуры. К примеру, слова, выбираемые для заголовочного регистра, зависят от языка.

Если вы делаете проверку безопасности, основанную на сравнении строк, то используйте InvariantCulture, чтобы гарантировать согласованность результатов вне зависимости от настроек системы или пользователя. Однако, инвариантная культура должна использоваться только процессами, которым требуются именно не зависимые от культуры результаты, вроде системных служб; в противном случае ваш код будет порождать результаты, которые могут быть лингвистически неверными.
Итак, сегодня единственным культурным отличием в регистре является правило, которое мы видели в турецком. Хотя здесь и есть потенциал для более глубокой проработки деталей - вроде тех, что упоминал Ruben. Ни один из этих случаев сейчас не обрабатывается. Но путь для них открыт: ничто не мешает их реализовать в будущем. И документация открыто указывает на возможность таких изменений в будущем (это насчёт изменения размера строк).

Это будет, однако, очень дорогостоящая операция - в терминах объёма исследований, которое необходимо будет провести. Поэтому лучше всего установить свои ожидания на что-то более реалистичное и не говорить про то, насколько этот метод имеет смысл в культурах (поскольку он не работает, по крайней мере не на сегодняшний день!).

Возможно, мы могли бы указать, что это не хуже размытого определения этой операции в стандарте Unicode. Что, вообще-то, довольно глупо - называть "стандартом" такие вещи.

Получается броский слоган - как вы думаете, может поместить это:
TextInfo.ToTitleCase - не отстойнее Unicode
на футболку? :-)

This post brought to you by "NJ", "nj", and "Nj" (U+01ca, U+01cc, and U+01cb, a.k.a. LATIN CAPITAL LETTER NJ, LATIN SMALL LETTER NJ, and LATIN CAPITAL LETTER N WITH SMALL LETTER J)
(a.k.a. формы букв Unicode ВЕРХНЕГО, НИЖНЕГО и ЗАГОЛОВОЧНОГО регистра)

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

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

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

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

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

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

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