Spec-Zone.ru › Ruby 3.4

Кодировки

Основы

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

  • Последовательностью 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]

Класс 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::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.

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

'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#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 может установить свою кодировку скрипта с помощью магического комментария в первой строке файла (или во второй, если в первой есть 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 = '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" (“�”) для кодировки Unicode, '?' в противном случае.

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

Spec-Zone.ru

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