Кодировки
Основы
Кодировка символов, часто сокращённо называемая кодировкой, — это соответствие между:
-
Последовательностью 8-битных байтов (каждый байт в диапазоне
0..255). -
Символами определённого набора символов.
Некоторые наборы символов содержат только однобайтовые символы; например, US-ASCII содержит 256 однобайтовых символов. Эта строка, закодированная в US-ASCII, состоит из шести символов, хранящихся в виде шести байтов:
s = 'Hello!'.encode(Encoding::US_ASCII) # => "Hello!" s.encoding # => #<Encoding:US-ASCII> s.bytes # => [72, 101, 108, 108, 111, 33]
Другие кодировки могут включать многобайтовые символы. Например, UTF-8 кодирует более миллиона символов, используя для каждого от одного до четырёх байтов. Символы с наименьшими значениями соответствуют символам ASCII и поэтому являются однобайтовыми:
s = 'Hello!' # => "Hello!" s.bytes # => [72, 101, 108, 108, 111, 33]
Другие символы, например символ евро, являются многобайтовыми:
s = "\u20ac" # => "€" s.bytes # => [226, 130, 172]
Класс Encoding
Объекты Encoding
Кодировки 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:
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 = Encoding::US_ASCII # => #<Encoding:US-ASCII> Encoding.default_external # => #<Encoding:US-ASCII>
Метод Encoding.default_internal возвращает внутреннюю кодировку по умолчанию:
Encoding.default_internal # => nil
Метод Encoding.default_internal= задаёт внутреннюю кодировку по умолчанию:
Encoding.default_internal = Encoding::US_ASCII # => #<Encoding: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(Encoding::ISO_8859_1) # => "\xA1\xA1"
s1 = "\xa1\xa1".force_encoding(Encoding::EUCJP) # => "\x{A1A1}"
Encoding.compatible?(s0, s1) # => nil
Кодировка строк
Объект Ruby String имеет кодировку, представленную экземпляром класса Encoding. Кодировку можно получить с помощью метода String#encoding.
Кодировка по умолчанию для строкового литерала — кодировка скрипта; см. раздел Кодировка скрипта.
's'.encoding # => #<Encoding:UTF-8>
Кодировка по умолчанию для строки, созданной методом String.new, следующая:
-
Если аргумент не указан — ASCII-8BIT.
-
Если аргументом является объект String — кодировка этой строки.
-
Если аргументом является строковый литерал — кодировка скрипта; см. раздел Кодировка скрипта.
В обоих случаях можно указать любую кодировку:
s = String.new(encoding: Encoding::UTF_8) # => ""
s.encoding # => #<Encoding:UTF-8>
s = String.new('foo', encoding: Encoding::BINARY) # => "foo"
s.encoding # => #<Encoding:BINARY (ASCII-8BIT)>
Кодировку строки можно изменить:
s = "R\xC3\xA9sum\xC3\xA9" # => "Résumé" s.encoding # => #<Encoding:UTF-8> s.force_encoding(Encoding::ISO_8859_1) # => "R\xC3\xA9sum\xC3\xA9" s.encoding # => #<Encoding:ISO-8859-1>
Изменение назначенной кодировки не меняет содержимое строки; меняется только способ его интерпретации:
s # => "R\xC3\xA9sum\xC3\xA9" s.force_encoding(Encoding::UTF_8) # => "Résumé"
Фактическое содержимое строки также можно изменить; см. раздел Перекодирование строки.
Вот несколько полезных методов проверки:
s = "abc".force_encoding(Encoding::UTF_8) # => "abc"
s.ascii_only? # => true
s = "abc\u{6666}".force_encoding(Encoding::UTF_8) # => "abc晦"
s.ascii_only? # => false
s = "\xc2\xa1".force_encoding(Encoding::UTF_8) # => "¡"
s.valid_encoding? # => true
s = "\xc2".force_encoding(Encoding::UTF_8) # => "\xC2"
s.valid_encoding? # => false
Кодировки символов и регулярных выражений
Строка, хранящаяся в объекте Symbol или Regexp, также имеет кодировку; её можно получить с помощью метода Symbol#encoding или Regexp#encoding.
Однако кодировка по умолчанию для них следующая:
-
US-ASCII, если все символы принадлежат US-ASCII.
-
В противном случае — кодировка скрипта; см. раздел Кодировка скрипта].
Кодировка файловой системы
Кодировка файловой системы — это кодировка по умолчанию для строки из файловой системы:
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 может задать свою кодировку с помощью специального комментария в первой строке файла (или во второй, если в первой строке указана строка shebang). Комментарий должен содержать слово 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в новую строку в соответствии с нормализацией Unicode. -
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 = Encoding::ISO_8859_1 int_enc = Encoding::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(Encoding::ISO_8859_3) # Raises Encoding::InvalidByteSequenceError. s.encode(Encoding::ISO_8859_3, invalid: :replace) # => "?foo?"
-
-
Для неопределённого символа:
-
:undef: nil(по умолчанию): вызвать исключение. -
:undef: :replace: заменить каждый неопределённый символ строкой-заменой.
Примеры:
s = "\x80foo\x80" "\x80".encode(Encoding::UTF_8, Encoding::BINARY) # Raises Encoding::UndefinedConversionError. s.encode(Encoding::UTF_8, Encoding::BINARY, undef: :replace) # => "�foo�"
-
-
Строка-замена:
-
:replace: nil(по умолчанию): установить строку-замену в значение по умолчанию:"\uFFFD"(«�») для кодировки Unicode и'?'в остальных случаях. -
:replace: some_string: установить строку-замену в заданное значениеsome_string; переопределяет:fallback.
Примеры:
s = "\xA5foo\xA5" options = {:undef => :replace, :replace => 'xyzzy'} s.encode(Encoding::UTF_8, Encoding::ISO_8859_3, **options) # => "xyzzyfooxyzzy" -
-
Резервная замена:
Можно указать один из следующих вариантов:
-
:fallback: nil(по умолчанию): без резервной замены. -
:fallback: hash_like_object: задать резервную замену указанным значениемhash_like_object; строка-замена —hash_like_object[X]. -
:fallback: method: задать резервную замену указанным значениемmethod; строка-замена —method(X). -
:fallback: proc: задать резервную замену указанным значениемproc; строка-замена —proc[X].
Примеры:
s = "\u3042foo\u3043" hash = {"\u3042" => 'xyzzy'} hash.default = 'XYZZY' s.encode(Encoding::US_ASCII, fallback: hash) # => "xyzzyfooXYZZY" def (fallback = "U+%.4X").escape(x) self % x.unpack("U") end "\u{3042}".encode(Encoding::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(Encoding::US_ASCII, xml: :text) # => "foo\"<&>\"barあ" s.encode(Encoding::US_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(Encoding::US_ASCII, cr_newline: true) # => "\r \r \r\r" s.encode(Encoding::US_ASCII, crlf_newline: true) # => "\r\n \r \r\r\n" s.encode(Encoding::US_ASCII, universal_newline: true) # => "\n \n \n"
-
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.