Программирование на языке Ruby
Шрифт:
Поскольку Ruby появился в Японии, он прекрасно справляется с двумя различными японскими кодировками (и ASCII). Не буду тратить время на рассказ о поддержке японского языка; если вы японец, то в вашем распоряжении сколько угодно книг по Ruby на этом языке. А для всех остальных наиболее распространённой кодировкой является Unicode. О ней мы и будем говорить в этой главе.
Но перед тем как перейти к деталям, познакомимся с некоторыми терминами. Называть вещи полезными именами — одна из основ мудрости!
• Байт — это просто восемь битов (хотя когда-то даже это было неверно). По традиции многие считают, что байт соответствует
• Кодовая позиция — один элемент воображаемой таблицы, с помощью которой представляется набор символов. Хотя это и не совсем верно, можете считать, что кодовые позиции взаимно однозначно отображаются на символы. Точнее будет сказать, что иногда для уникального указания символа требуется несколько кодовых позиций.
• Глиф (печатный знак) — визуальное представление кодовой позиции. Хотя интуитивно это и не совсем очевидно, символ и его визуальное представление - разные вещи. (Я могу открыть текстовый редактор и набрать прописную А десятком разных шрифтов, но все это будет один и тот же символ А.)
• Понятие графемы близко к глифу, но о графемах мы говорим в контексте языка, а не программного обеспечения. Графема может быть комбинацией (простой или не очень) двух и более глифов. Так пользователь воспринимает символ в контексте своего родного языка. Разница настолько тонкая, что большинство программистов могут о ней никогда не задумываться.
Что же тогда такое символ? Даже в мире Unicode нет четкого понимания этого предмета, поскольку языки ведут себя по-разному, а программисты мыслят иначе, чем прочие люди. Будем говорить, что символ — это абстракция написания знака, который визуально может быть представлен одним или несколькими способами.
Перейдем к конкретике. Сначала я хочу познакомить вас с нотацией. Традиционно кодовые позиции Unicode записываются как U+, а затем четыре или более шестнадцатеричных цифр в верхнем регистре. То, что мы называем латинской буквой А, можно представить в виде U+0041.
Теперь возьмем букву 'e (строчная е с акутом). Ее можно представить в Unicode двумя способами. Во-первых, это одна кодовая позиция U+00E9 (СТРОЧНАЯ ЛАТИНСКАЯ Е С АКУТОМ). С другой стороны, это сочетание двух кодовых позиций: строчная е + диакритический знак акут — U+0065 и U+0301. Иными словами, СТРОЧНАЯ ЛАТИНСКАЯ Е, за которой следует АКУТ.
Обе формы одинаково правильны. Более короткая называется монолитной (precomposed) формой. Однако имейте в виду, что не для каждого языка имеются монолитные варианты, поэтому не всегда можно свести подобный символ к одной кодовой позиции.
Я назвал Unicode кодировкой, но это не вполне верно. Unicode отображает символы на кодовые позиции; существуют разные способы отобразить кодовые позиции на двоичное представление. По существу, Unicode — это семейство кодировок.
Возьмем, к примеру, строку
Естественнее всего сохранить их в виде простой последовательности байтов.
Такая кодировка называется
Заметим, однако, что каждый второй байт в этой последовательности нулевой. Это не просто совпадение, английский язык редко выходит за пределы кодовой позиции
И это наблюдение подводит нас к идее кодировки UTF-8. В ней «традиционные» символы представлены одним байтом, а остальные — несколькими. Вот как записывается та же строка в кодировке UTF-8:
Мы всего лишь избавились от нулей. Однако более важен тот факт, что мы получили обычную кодировку ASCII. Так и задумано: «простой ASCII» можно считать собственным подмножеством UTF-8.
Отсюда, в частности, следует, что при интерпретации текста в кодировке UTF-8 как ASCII-текста он выглядит «как обычно» (особенно если это преимущественно англоязычный текст). Иногда вы видите, что браузер или другое приложение отображает английский текст правильно, но местами появляются «крокозябры». Это, скорее всего, означает, что программа сделала неверные предположения об используемой кодировке.
Итак, можно сказать, что UTF-8 экономит память. Конечно, я снова становлюсь на англоцентрическую точку зрения (по крайней мере, ASCII-центрическую). Если текст в основном состоит из ASCII-символов, то да, память экономится, но для других языков, например греческого или русского, размер строк даже увеличится.
Еще одно очевидное достоинство UTF-8 — «обратная совместимость» с кодировкой ASCII, которая, по-видимому, все еще остается самой распространенной однобайтовой кодировкой в мире. Наконец, у UTF-8 есть некоторые особенности, делающие ее удобной для программистов.
Во-первых, байты, входящие в состав многобайтовых символов, тщательно подобраны. Нулевой байт (ASCII 0) никогда не встречается в качестве n-ого байта в последовательности (где n > 1); то же самое справедливо для таких распространенных символов, как косая черта (обычно используется для разделения компонентов пути к файлу). На самом деле никакой байт из диапазона 0x00-0x7F не может быть частью никакого другого символа.
Второй байт многобайтового символа однозначно определяет, сколько байтов за ним следует. Этот второй байт всегда выбирается из диапазона от
UTF-8 — одна из самых распространенных и гибких кодировок в мире. Она применяется с начала 1990-х годов и является кодировкой по умолчанию XML-документов. В этой главе мы будем иметь дело главным образом именно с UTF-8.
4.2. Кодировки в пост-ASCII мире
«Век ASCII» прошел, хотя не все еще осознали этот факт. Многие допущения, которые программисты делали в прошлом, уже несправедливы. Нам необходимо новое мышление.
Есть две идеи, которые, на мой взгляд, являются основополагающими, почти аксиомами. Во-первых, строка не имеет внутренней интерпретации. Она должна интерпретироваться в соответствии с некоторым внешним стандартом. Во-вторых, байт и символ — не одно и то же; символ может состоять из одного или нескольких байтов. Есть и другие уроки, но это самое важное.