Spec-Zone.ru › Ruby 3.4

Кодировки

Основы

Кодировка символов, часто сокращённо называемая кодировкой, представляет собой соответствие между:

  • Последовательностью 8-битных байтов (каждый байт в диапазоне 0..255).

  • Символами определённого набора символов.

Некоторые наборы символов содержат только однобайтовые символы; например, US-ASCII содержит 256 однобайтовых символов. Эта строка в кодировке US-ASCII состоит из шести символов, сохранённых в виде шести байтов:

s = 'Hello!'.encode('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, упомянутый выше, также возвращает кодировку Encoding по умолчанию для каждого из этих специальных названий:

  • 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? возвращает информацию о совместимости двух заданных объектов кодировок (то есть о возможности их конкатенации); возвращает кодировку Encoding объединённой строки или 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.

Кодировкой по умолчанию для строкового литерала является кодировка скрипта; см. Кодировка скрипта.

's'.encoding # => #<Encoding:UTF-8>

Кодировка по умолчанию для строки, созданной методом String.new, следующая:

  • Для аргумента-объекта String — кодировка этой строки.

  • Для строкового литерала — кодировка скрипта; см. Кодировка скрипта.

В обоих случаях можно указать любую кодировку:

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 или Regexp, также имеет кодировку; её можно получить с помощью метода Symbol#encoding или Regexp#encoding.

Однако кодировка по умолчанию для этих объектов следующая:

  • US-ASCII, если все символы относятся к US-ASCII.

  • В противном случае — кодировка скрипта; см. (Кодировка скрипта] в Encoding)[rdoc-ref:encodings.rdoc.

Кодировка файловой системы

Кодировка файловой системы — это кодировка по умолчанию для строки из файловой системы:

Encoding.find("filesystem") # => #<Encoding:UTF-8>

Кодировка локали

Кодировка локали — это кодировка по умолчанию для строки из окружения, за исключением строки из файловой системы:

Encoding.find('locale') # => #<Encoding:IBM437>

Кодировки потоков

Некоторые объекты потоков могут иметь две кодировки; к ним относятся экземпляры:

  • IO.

  • File.

  • ARGF.

  • StringIO.

Эти две кодировки:

  • Внешняя кодировка, определяющая кодировку потока.

  • Внутренняя кодировка, которая (если не 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>

Транскодирование

Транскодирование — это процесс преобразования последовательности символов из одной кодировки в другую.

По возможности символы остаются теми же, но байты, которыми они представлены, могут измениться.

Способ обработки символов, которые невозможно представить в целевой кодировке, можно указать с помощью @Параметры+кодировки.

Транскодирование строки

Каждый из этих методов транскодирует строку:

  • 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 = '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 (по умолчанию): установить строку-замену в значение по умолчанию: "\uFFFD" («�») для кодировки Unicode и '?' в остальных случаях.

    • :replace: some_string: установить для строки-замены заданное значение 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: установить для резервного варианта замены заданное значение 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('ASCII', fallback: hash) # => "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; заменить каждый неопределённый символ его числовой ссылкой на символ в шестнадцатеричном формате с прописными буквами, за исключением следующих случаев:

      • & заменяется на &amp;.

      • < заменяется на &lt;.

      • > заменяется на &gt;.

    • :xml: :attr: трактовать исходный текст как значение атрибута XML; заменить каждый неопределённый символ его числовой ссылкой на символ в шестнадцатеричном формате с прописными буквами, за исключением следующих случаев:

      • Строка-замена r заключается в двойные кавычки ("r").

      • Каждая встроенная двойная кавычка заменяется на &quot;.

      • & заменяется на &amp;.

      • < заменяется на &lt;.

      • > заменяется на &gt;.

    Примеры:

    s = 'foo"<&>"bar' + "\u3042"
    s.encode('ASCII', xml: :text) # => "foo\"&lt;&amp;&gt;\"bar&#x3042;"
    s.encode('ASCII', xml: :attr) # => "\"foo&quot;&lt;&amp;&gt;&quot;bar&#x3042;\""
    
  • Символы новой строки:

    Можно указать один из следующих параметров:

    • :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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API