Кодировки
Основы
A кодировка символов, часто сокращённая до кодировки, представляет собой отображение:
-
Последовательности байтов по 8 бит (каждый байт в диапазоне
0..255). -
Символов в определённом наборе символов.
Некоторые наборы символов содержат только символы длиной в 1 байт; например, US-ASCII содержит 256 символов длиной в 1 байт. Эта строка, закодированная в US-ASCII, содержит шесть символов, которые хранятся как шесть байтов:
s = 'Hello!'.encode('US-ASCII') # => "Hello!"
s.encoding # => #<Encoding:US-ASCII>
s.bytes # => [72, 101, 108, 108, 111, 33]
Другие кодировки могут использовать многобайтовые символы. Например, UTF-8 кодирует более миллиона символов, каждый из которых кодируется от одного до четырёх байтов. Самые низкие значения этих символов соответствуют символам ASCII и поэтому занимают 1 байт:
s = 'Hello!' # => "Hello!" s.bytes # => [72, 101, 108, 108, 111, 33]
Другие символы, такие как символ евро, имеют многобайтовую кодировку:
s = "\u20ac" # => "€" s.bytes # => [226, 130, 172]
Класс кодировки
Объекты кодировки
Кодировки Ruby определяются константами в классе Encoding. Для каждой из этих констант может существовать только один экземпляр Encoding. Метод Encoding.list возвращает массив объектов Encoding (по одному для каждой константы):
Encoding.list.size # => 103 Encoding.list.first.class # => Encoding Encoding.list.take(3) # => [#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>]
Имена и псевдонимы
Метод Encoding#name возвращает имя кодировки:
Encoding::ASCII_8BIT.name # => "ASCII-8BIT" Encoding::WINDOWS_31J.name # => "Windows-31J"
Объект Encoding имеет ноль или более псевдонимов; метод Encoding#names возвращает массив, содержащий имя и все псевдонимы:
Encoding::ASCII_8BIT.names # => ["ASCII-8BIT", "BINARY"] Encoding::WINDOWS_31J.names #=> ["Windows-31J", "CP932", "csWindows31J", "SJIS", "PCK"]
Метод Encoding.aliases возвращает хеш всех пар псевдоним/имя:
Encoding.aliases.size # => 71 Encoding.aliases.take(3) # => [["BINARY", "ASCII-8BIT"], ["CP437", "IBM437"], ["CP720", "IBM720"]]
Метод Encoding.name_list возвращает массив всех имён и псевдонимов кодировок:
Encoding.name_list.size # => 175 Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
Метод name_list возвращает больше записей, чем метод list, поскольку он включает как имена, так и их псевдонимы.
Метод Encoding.find возвращает Encoding для заданного имени или псевдонима, если он существует:
Encoding.find("US-ASCII") # => #<Encoding:US-ASCII>
Encoding.find("US-ASCII").class # => Encoding
Значения по умолчанию
Метод Encoding.find, выше, также возвращает значение по умолчанию для каждой из этих специальных названий:
-
external: кодировка по умолчанию для внешнего потока:Encoding.find("external") # => #<Encoding:UTF-8> -
internal: кодировка по умолчанию для внутреннего потока (может бытьnil):Encoding.find("internal") # => nil -
locale: кодировка по умолчанию для строки из среды:Encoding.find("locale") # => #<Encoding:UTF-8> # Linux Encoding.find("locale") # => #<Encoding:IBM437> # Windows -
filesystem: кодировка по умолчанию для строки из файловой системы:Encoding.find("filesystem") # => #<Encoding:UTF-8>
Метод Encoding.default_external возвращает значение по умолчанию для внешней кодировки:
Encoding.default_external # => #<Encoding:UTF-8>
Метод Encoding.default_external= устанавливает это значение:
Encoding.default_external = 'US-ASCII' # => "US-ASCII" Encoding.default_external # => #<Encoding:US-ASCII>
Метод Encoding.default_internal возвращает значение по умолчанию для внутренней кодировки:
Encoding.default_internal # => nil
Метод Encoding.default_internal= устанавливает значение по умолчанию для внутренней кодировки:
Encoding.default_internal = 'US-ASCII' # => "US-ASCII" Encoding.default_internal # => #<Encoding:US-ASCII>
Совместимые кодировки
Метод Encoding.compatible? возвращает, совместимы ли два данных объекта с точки зрения кодировок (т. е. могут ли они быть конкатенированы); возвращает кодировку конкатенированной строки или nil в случае несовместимости:
rus = "\u{442 435 441 442}"
eng = 'text'
Encoding.compatible?(rus, eng) # => #<Encoding:UTF-8>
s0 = "\xa1\xa1".force_encoding('iso-8859-1') # => "\xA1\xA1"
s1 = "\xa1\xa1".force_encoding('euc-jp') # => "\x{A1A1}"
Encoding.compatible?(s0, s1) # => nil
Кодировка строк
Объект Ruby String имеет кодировку, которая является экземпляром класса Encoding. Кодировку можно получить с помощью метода String#encoding.
Значение по умолчанию для кодировки строковой литералы — это кодировка скрипта (см. Кодировка скрипта на Encoding):
's'.encoding # => #<Encoding:UTF-8>
Значение по умолчанию для кодировки строки, созданной с помощью метода String.new, — это:
-
Для строкового аргумента — кодировка этой строки.
-
Для строковой литералы — кодировка скрипта (см. Кодировка скрипта на
Encoding).
В любом случае можно указать любую кодировку:
s = String.new(encoding: 'UTF-8') # => ""
s.encoding # => #<Encoding:UTF-8>
s = String.new('foo', encoding: 'ASCII-8BIT') # => "foo"
s.encoding # => #<Encoding:ASCII-8BIT>
Кодировку строки можно изменить:
s = "R\xC3\xA9sum\xC3\xA9" # => "Résumé"
s.encoding # => #<Encoding:UTF-8>
s.force_encoding('ISO-8859-1') # => "R\xC3\xA9sum\xC3\xA9"
s.encoding # => #<Encoding:ISO-8859-1>
Изменение назначенной кодировки не изменяет содержимого строки; оно изменяет только способ интерпретации содержимого:
s # => "R\xC3\xA9sum\xC3\xA9"
s.force_encoding('UTF-8') # => "Résumé"
Фактическое содержимое строки также может быть изменено; см. Преобразование кодировки строки.
Вот несколько полезных методов запроса:
s = "abc".force_encoding("UTF-8") # => "abc"
s.ascii_only? # => true
s = "abc\u{6666}".force_encoding("UTF-8") # => "abc晦"
s.ascii_only? # => false
s = "\xc2\xa1".force_encoding("UTF-8") # => "¡"
s.valid_encoding? # => true
s = "\xc2".force_encoding("UTF-8") # => "\xC2"
s.valid_encoding? # => false
Кодировки символов и регулярных выражений
Строка, хранящаяся в объекте Symbol или Regexp, также имеет кодировку; кодировку можно получить с помощью метода Symbol#encoding или Regexp#encoding.
Однако значение по умолчанию для этих объектов:
-
US-ASCII, если все символы — US-ASCII.
-
Кодировка скрипта в противном случае (см. Кодировка скрипта на
Encoding).
Кодировка файловой системы
Кодировка файловой системы — это значение по умолчанию для кодировки строки из файловой системы:
Encoding.find("filesystem") # => #<Encoding:UTF-8>
Кодировка локали
Кодировка локали — это значение по умолчанию для кодировки строки из среды, кроме файловой системы:
Encoding.find('locale') # => #<Encoding:IBM437>
Кодировки потоков
Некоторые объекты потоков могут иметь две кодировки; эти объекты включают экземпляры:
Две кодировки:
- Внешняя кодировка, которая определяет кодировку потока.
- Внутренняя кодировка, которая (если не
nil), указывает кодировку, используемую для строки, созданной из потока.
Внешняя кодировка
Внешняя кодировка, которая является объектом Encoding, определяет, как байты, считанные из потока, интерпретируются как символы.
Значение по умолчанию для внешней кодировки:
-
UTF-8 для текстового потока.
-
ASCII-8BIT для двоичного потока.
Значение по умолчанию для внешней кодировки возвращается методом Encoding.default_external, и может быть установлено:
-
Опции командной строки Ruby
--external_encodingили-E.
Вы также можете установить значение по умолчанию для внешней кодировки, используя метод Encoding.default_external=, но это может вызвать проблемы; строки, созданные до и после изменения, могут иметь разные кодировки.
Для объекта IO или File внешняя кодировка может быть установлена с помощью:
-
Опции открытия
external_encodingилиencoding, при создании объекта; см. Опции открытия.
Для объекта IO, File, ARGF или StringIO внешняя кодировка может быть установлена с помощью:
-
Методов
set_encodingили (кроме ARGF)set_encoding_by_bom.
Внутренняя кодировка
Внутренняя кодировка, которая является объектом Encoding или nil, определяет, как символы, считанные из потока, преобразуются в символы во внутренней кодировке; эти символы становятся строкой, чья кодировка установлена в кодировку внутренней кодировки.
Значение по умолчанию для внутренней кодировки — nil (без преобразования). Оно возвращается методом Encoding.default_internal и может быть установлено:
-
Опции командной строки Ruby
--internal_encodingили-E.
Вы также можете установить значение по умолчанию для внутренней кодировки, используя метод Encoding.default_internal=, но это может вызвать проблемы; строки, созданные до и после изменения, могут иметь разные кодировки.
Для объекта IO или File внутренняя кодировка может быть установлена с помощью:
-
Опций открытия
internal_encodingилиencoding, при создании объекта; см. Опции открытия.
Для объекта IO, File, ARGF или StringIO внутренняя кодировка может быть установлена с помощью:
-
Метода
set_encoding.
Кодировка скрипта
Скрипт Ruby имеет кодировку скрипта, которую можно получить:
__ENCODING__ # => #<Encoding:UTF-8>
Значение по умолчанию для кодировки скрипта — UTF-8; файл исходного кода Ruby может установить свою кодировку скрипта с помощью магического комментария в первой строке файла (или второй, если в первой есть шебанг). Комментарий должен содержать слово coding или encoding, за которым следует двоеточие, пробел и имя Encoding или псевдоним:
# encoding: ISO-8859-1 __ENCODING__ #=> #<Encoding:ISO-8859-1>
Преобразование кодировки
Преобразование кодировки — это процесс изменения последовательности символов из одной кодировки в другую.
По возможности символы остаются неизменными, но байты, которые их представляют, могут измениться.
Обработка символов, которые не могут быть представлены в кодировке назначения, может быть задана с помощью @Encoding+Options.
Преобразование кодировки строки
Каждый из этих методов преобразует кодировку строки:
-
String#encode: Преобразуетselfв новую строку в соответствии с заданными кодировками и параметрами. -
String#encode!: АналогичноString#encode, но преобразуетselfнепосредственно на месте. -
String#scrub: Преобразуетselfв новую строку, заменяя некорректные последовательности байтов заданной или по умолчанию строкой замены. -
String#scrub!: АналогичноString#scrub, но преобразуетselfнепосредственно на месте. -
String#unicode_normalize: Преобразуетselfв новую строку в соответствии с нормализацией Юникода. -
String#unicode_normalize!: АналогичноString#unicode_normalize, но преобразуетselfнепосредственно на месте.
Преобразование потока
Каждый из этих методов может преобразовать поток; выполняется ли это зависит от внешних и внутренних кодировок:
-
IO.foreach: Выводит каждую строку заданного потока в блок. -
IO.new: Создаёт и возвращает новый объект IO для заданного целочисленного дескриптора файла. -
IO.open: Создаёт новый объект IO. -
IO.pipe: Создаёт связанную пару объектов IO для чтения и записи. -
IO.popen: Создаёт объект IO для взаимодействия с дочерним процессом. -
IO.read: Возвращает строку со всеми или частью байтов из заданного потока. -
IO.readlines: Возвращает массив строк, которые являются строками из заданного потока. -
IO.write: Записывает заданную строку в заданный поток.
В этом примере записывается строка в файл, кодируя её как ISO-8859-1, затем считывается файл в новую строку, кодируя её как UTF-8:
s = "R\u00E9sum\u00E9" path = 't.tmp' ext_enc = 'ISO-8859-1' int_enc = 'UTF-8' File.write(path, s, external_encoding: ext_enc) raw_text = File.binread(path) transcoded_text = File.read(path, external_encoding: ext_enc, internal_encoding: int_enc) p raw_text p transcoded_text
Вывод:
"R\xE9sum\xE9" "Résumé"
Параметры кодировки
Ряд методов в ядре Ruby принимают ключевые аргументы в качестве параметров кодировки.
Некоторые параметры задают или используют строку замены, которая используется в определённых операциях преобразования кодировки. Строка замены может быть в любой кодировке, которая может быть преобразована в кодировку целевой строки.
Эти пары ключ-значение задают параметры кодировки:
-
Для некорректной последовательности байтов:
-
:invalid: nil(по умолчанию): Сгенерировать исключение. -
:invalid: :replace: Заменить каждую некорректную последовательность байтов строкой замены.
Примеры:
s = "\x80foo\x80" s.encode('ISO-8859-3') # Raises Encoding::InvalidByteSequenceError. s.encode('ISO-8859-3', invalid: :replace) # => "?foo?" -
-
Для неопределённого символа:
-
:undef: nil(по умолчанию): Сгенерировать исключение. -
:undef: :replace: Заменить каждый неопределённый символ строкой замены.
Примеры:
s = "\x80foo\x80" "\x80".encode('UTF-8', 'ASCII-8BIT') # Raises Encoding::UndefinedConversionError. s.encode('UTF-8', 'ASCII-8BIT', undef: :replace) # => "�foo�" -
-
Строка замены:
-
:replace: nil(по умолчанию):Setстроку замены на значение по умолчанию:"\uFFFD"(“�”) для кодировки Юникода,'?'в противном случае. -
:replace: some_string:Setстроку замены на заданнуюsome_string; переопределяет:fallback.
Примеры:
s = "\xA5foo\xA5" options = {:undef => :replace, :replace => 'xyzzy'} s.encode('UTF-8', 'ISO-8859-3', **options) # => "xyzzyfooxyzzy" -
-
Падение замены:
Может быть указан один из этих параметров:
-
:fallback: nil(по умолчанию): Нет падения замены. -
:fallback: hash_like_object:Setпадение замены на заданнуюhash_like_object; строка замены —hash_like_object[X]. -
:fallback: method:Setпадение замены на заданнуюmethod; строка замены —method(X). -
:fallback: proc:Setпадение замены на заданнуюproc; строка замены —proc[X].
Примеры:
s = "\u3042foo\u3043" hash = {"\u3042" => 'xyzzy'} hash.default = 'XYZZY' s.encode('ASCII', fallback: h) # => "xyzzyfooXYZZY" def (fallback = "U+%.4X").escape(x) self % x.unpack("U") end "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)) # => "U+3042" proc = Proc.new {|x| x == "\u3042" ? 'xyzzy' : 'XYZZY' } s.encode('ASCII', fallback: proc) # => "XYZZYfooXYZZY" -
-
Сущности XML:
Может быть указан один из этих параметров:
-
:xml: nil(по умолчанию): Нет обработки XML-сущностей. -
:xml: :text: Обработать исходный текст как XML; заменить каждый неопределённый символ его шестнадцатеричным числовым кодом в верхнем регистре, за исключением того, что:-
&заменяется на&. -
<заменяется на<. -
>заменяется на>.
-
-
:xml: :attr: Обработать исходный текст как значение атрибута XML; заменить каждый неопределённый символ его шестнадцатеричным числовым кодом в верхнем регистре, за исключением того, что:-
Строка замены
rзаключена в двойные кавычки ("r"). -
Каждая вложенная двойная кавычка заменяется на
". -
&заменяется на&. -
<заменяется на<. -
>заменяется на>.
-
Примеры:
s = 'foo"<&>"bar' + "\u3042" s.encode('ASCII', xml: :text) # => "foo\"<&>\"barあ" s.encode('ASCII', xml: :attr) # => "\"foo"<&>"barあ\"" -
-
Символы новой строки:
Может быть указан один из этих параметров:
-
:cr_newline: true: Заменить каждый символ новой строки ("\n") символом возврата каретки ("\r"). -
:crlf_newline: true: Заменить каждый символ новой строки ("\n") строкой возврата каретки/новой строки ("\r\n"). -
:universal_newline: true: Заменить каждый символ возврата каретки ("\r") и каждую строку возврата каретки/новой строки ("\r\n") символом новой строки ("\n").
Примеры:
s = "\n \r \r\n" # => "\n \r \r\n" s.encode('ASCII', cr_newline: true) # => "\r \r \r\r" s.encode('ASCII', crlf_newline: true) # => "\r\n \r \r\r\n" s.encode('ASCII', universal_newline: true) # => "\n \n \n" -
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.