Чтение онлайн

ЖАНРЫ

Программирование на языке Ruby
Шрифт:

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

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"

# Для справки: французское слово "'ep'ee"

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

eacute = ""

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

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

p eacute # "\303\251"

p sword # "\303\251p\303\251e"

$KCODE = "u"

p eacute # "'e"

p sword # "'ep'ee"

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

$KCODE = "n"

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

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

puts letters.size # 6

$KCODE = "u"

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

# [["'e"], ["p"], ["'e"], ["e"]]

puts letters.size # 4

Библиотека

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

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

jcode
.

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

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

"'eр" + "'eе" # "'ep'ee"

"'eр" << "'eе" # "'ep'ee"

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

специально делать не нужно:

"'ep'ee".include?("'e") # true

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

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

Метод

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

$KCODE = "u"

require 'jcode'

sword = "'ep'ee"

sword.jlength # 4

sword.length # 6

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

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

$KCODE = "u"

sword.upcase # "'EP'EE"

sword.capitalize # "'ep'ee"

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

Возможно, вы думаете, что неакцентированные символы в некотором смысле эквивалентны своим акцентированным вариантам. Это почти всегда не так. Здесь мы имеем дело с разными символами. Убедимся в этом на примере метода

count
:

$KCODE = "u"

sword.count("e") # 1 (не 3)

Но для составных (не монолитных) символов верно прямо противоположное. В этом случае латинская буква распознается.

Метод

count
возвращает сбивающий с толку результат, когда ему передается многобайтовый символ. Метод
jcount
ведет себя в этом случае правильно:

$KCODE = "u"

sword.count("e'e") # 5 (не 3)

sword.jcount("e'e") # 3

Существует вспомогательный метод

mbchar?
, который определяет, есть ли в строке многобайтовые символы.

$KCODE = "u"

sword.mbchar? # 0 (смещение первого многобайтового символа)

"foo".mbchar? # nil

В библиотеке

jcode
переопределены также методы
chop
,
delete
,
squeeze
,
succ
,
tr
и
tr_s
. Применяя их в режиме UTF-8, помните, что вы работаете с версиями, «знающими о многобайтовости». При попытке манипулировать многобайтовыми строками без библиотеки
jcode
вы можете получить странные или ошибочные результаты.

Поделиться с друзьями: