Программирование на языке Ruby - Страница 53

Ознакомительная версия. Доступно 54 страниц из 266.
Изменить размер шрифта:

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="UTF-8"
). Важен только первый символ.

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

Установив значение

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

$KCODE = "n"

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

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

eacute = ""

eacute << 0303 << 0251 # U+00E9

sword = eacute + "p" + eacute + "e"

p eacute # "303251"

p sword # "303251p303251e"

$KCODE = "u"

p eacute # "é"

p sword  # "épée"

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

$KCODE = "n"

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

# [["303"], ["251"], ["p"], ["303"], ["251"], ["e"]]

puts letters.size # 6

$KCODE = "u"

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

# [["é"], ["p"], ["é"], ["e"]]

puts letters.size # 4

Библиотека

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

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

jcode
.

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

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

"éр" + "éе"  # "épée"

"éр" << "éе" # "épée"

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

"épée".include?("é") # true

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

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

Метод

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

$KCODE = "u"

require 'jcode'

sword = "épée"

sword.jlength # 4

sword.length  # 6

Такие методы, как

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

$KCODE = "u"

sword.upcase     # "ÉPÉE"

sword.capitalize # "épée"

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

Оригинальный текст книги читать онлайн бесплатно в онлайн-библиотеке Knigger.com