Добавить в цитаты Настройки чтения

Страница 53 из 53

UTF-8 — одна из самых распространенных и гибких кодировок в мире. Она применяется с начала 1990-х годов и является кодировкой по умолчанию XML-документов. В этой главе мы будем иметь дело главным образом именно с UTF-8.

4.2. Кодировки в пост-ASCII мире

«Век ASCII» прошел, хотя не все еще осознали этот факт. Многие допущения, которые программисты делали в прошлом, уже несправедливы. Нам необходимо новое мышление.

Есть две идеи, которые, на мой взгляд, являются основополагающими, почти аксиомами. Во-первых, строка не имеет внутренней интерпретации. Она должна интерпретироваться в соответствии с некоторым внешним стандартом. Во-вторых, байт и символ — не одно и то же; символ может состоять из одного или нескольких байтов. Есть и другие уроки, но это самое важное.

Эти факты оказывают на программирование тонкое влияние. Рассмотрим сначала, как следует работать с символьными строками по-современному.

4.2.1. Библиотека jcode и переменная $KCODE

Чтобы использовать в Ruby разные наборы символов, вы должны знать о глобальной переменной $KCODE, от значения которой зависит поведение многих системных методов, манипулирующих строками. (Кстати говоря, буква K — напоминание о кандзи, одной из иероглифических азбук в японском языке.) Эта переменная принимает одно из пяти стандартных значений, каждое из которых представлено одной буквой, неважно — строчной или прописной (ASCII и NONE — одно и то же).

a ASCII

n NONE (ASCII)

е EUC

s SJIS

u UTF-8

Для ясности можно пользоваться и полными названиями (например, $KCODE<b>=&quot;</b>UTF-8&quot;). Важен только первый символ.

О кодировке ASCII мы уже знаем. EUC и Shift-JIS (SJIS) нам малоинтересны. Мы сосредоточимся на значении UTF-8.

Установив значение $KCODE, вы задаром получаете весьма богатую функциональность. Например, метод inspect (он автоматически вызывается при обращении к методу p для печати объекта в читаемом виде) обычно учитывает текущее значение $KCODE.

$KCODE = &quot;n&quot;

# Для справки: французское слово &quot;épée&quot;

# обозначает разновидность меча (sword).

eacute = &quot;&quot;

eacute &lt;&lt; 0303 &lt;&lt; 0251 # U+00E9

sword = eacute + &quot;p&quot; + eacute + &quot;e&quot;

p eacute # &quot;303251&quot;

p sword # &quot;303251p303251e&quot;

$KCODE = &quot;u&quot;

p eacute # &quot;é&quot;

p sword  # &quot;épée&quot;

Регулярные выражения в режиме UTF-8 тоже становятся несколько «умнее».

$KCODE = &quot;n&quot;

letters = sword.scan(/(.)/)





# [[&quot;303&quot;], [&quot;251&quot;], [&quot;p&quot;], [&quot;303&quot;], [&quot;251&quot;], [&quot;e&quot;]]

puts letters.size # 6

$KCODE = &quot;u&quot;

letters = sword.scan(/(.)/)

# [[&quot;é&quot;], [&quot;p&quot;], [&quot;é&quot;], [&quot;e&quot;]]

puts letters.size # 4

Библиотека jcode предоставляет также несколько полезных методов, например jlength и each_char. Рекомендую включать эту библиотеку с помощью директивы require всякий раз, как вы работаете с кодировкой UTF-8.

В следующем разделе мы снова рассмотрим некоторые типичные операции со строками и регулярными выражениями. Заодно поближе познакомимся с jcode.

4.2.2. Возвращаясь к строкам и регулярным выражениям

При работе с UTF-8 некоторые операции ничем не отличаются. Например, конкатенация строк выполняется так же, как и раньше:

&quot;éр&quot; + &quot;éе&quot;  # &quot;épée&quot;

&quot;éр&quot; &lt;&lt; &quot;éе&quot; # &quot;épée&quot;

Поскольку UTF-8 не имеет состояния, то для проверки вхождения подстроки тоже ничего специально делать не нужно:

&quot;épée&quot;.include?(&quot;é&quot;) # true

Однако при написании интернациональной программы некоторые типичные допущения все же придется переосмыслить. Ясно, что символ больше не эквивалентен байту. При подсчете символов или байтов надо думать о том, что именно мы хотим сосчитать и для чего. То же относится к числу итераций.

По общепринятому соглашению, кодовую позицию часто представляют себе как «программистский символ». Это еще одна полуправда, но иногда она оказывается полезной.

Метод jlength возвращает число кодовых позиций в строке, а не байтов. Если нужно получить число байтов, пользуйтесь методом length.

$KCODE = &quot;u&quot;

require 'jcode'

sword = &quot;épée&quot;

sword.jlength # 4

sword.length  # 6

Такие методы, как upcase и capitalize, обычно неправильно работают со специальными символами. Это ограничение текущей версии Ruby. (Не стоит считать ошибкой, поскольку получить представление слова с первой прописной буквой довольно трудно; такая задача просто не решается в схеме интернационализации Ruby. Считайте, что это нереализованное поведение.)

$KCODE = &quot;u&quot;

sword.upcase     # &quot;ÉPÉE&quot;

sword.capitalize # &quot;épée&quot;

Если вы не пользуетесь монолитной формой, то в некоторых случаях метод может сработать, поскольку латинские буквы отделены от диакритических знаков. Но в общем случае работать не будет — в частности, для турецкого, немецкого, голландского и любого другого языка с нестандартными правилами преобразования регистра.

Конец ознакомительного фрагмента. Полная версия книги есть на сайте ЛитРес.