Упакованные данные
Некоторые методы ядра Ruby работают с упаковкой и распаковкой данных:
-
Метод
Array#pack: Форматирует каждый элемент в массивеselfв двоичную строку; возвращает эту строку. -
Метод
String#unpack: Извлекает данные из строкиself, формируя объекты, которые становятся элементами нового массива; возвращает этот массив. -
Метод
String#unpack1: Делает то же самое, но распаковывает и возвращает только первый извлеченный объект.
Каждый из этих методов принимает строку template, состоящую из нуля или более директивы символов, каждый из которых следует за нулем или более модификаторов символов.
Примеры (директива 'C' указывает «беззнаковый символ»):
[65].pack('C') # => "A" # One element, one directive.
[65, 66].pack('CC') # => "AB" # Two elements, two directives.
[65, 66].pack('C') # => "A" # Extra element is ignored.
[65].pack('') # => "" # No directives.
[65].pack('CC') # Extra directive raises ArgumentError.
'A'.unpack('C') # => [65] # One character, one directive.
'AB'.unpack('CC') # => [65, 66] # Two characters, two directives.
'AB'.unpack('C') # => [65] # Extra character is ignored.
'A'.unpack('CC') # => [65, nil] # Extra directive generates nil.
'AB'.unpack('') # => [] # No directives.
Строка template может содержать любое сочетание допустимых директив (директива 'c' указывает «знаковый символ»):
[65, -1].pack('cC') # => "A\xFF"
"A\xFF".unpack('cC') # => [65, 255]
Строка template может содержать пробелы (которые игнорируются) и комментарии, каждый из которых начинается с символа '#' и продолжается до и включая следующий символ перевода строки:
[0,1].pack(" C #foo \n C ") # => "\x00\x01"
"\0\1".unpack(" C #foo \n C ") # => [0, 1]
Любая директива может быть дополнена одним из этих модификаторов:
-
'*'- Директива должна применяться столько раз, сколько необходимо:[65, 66].pack('C*') # => "AB" 'AB'.unpack('C*') # => [65, 66] -
Integercount- Директива должна применятьсяcountраз:[65, 66].pack('C2') # => "AB" [65, 66].pack('C3') # Raises ArgumentError. 'AB'.unpack('C2') # => [65, 66] 'AB'.unpack('C3') # => [65, 66, nil]Примечание: Директивы в
%w[A a Z m]используютcountпо-разному; см. Директивы строк.
Если элементы не соответствуют предоставленной директиве, кодируются только младшие биты:
[257].pack("C").unpack("C") # => [1]
Метод упаковки
Метод Array#pack принимает необязательный ключевой аргумент buffer , который указывает целевую строку (вместо новой строки):
[65, 66].pack('C*', buffer: 'foo') # => "fooAB"
Метод может принимать блок:
# Packed string is passed to the block.
[65, 66].pack('C*') {|s| p s } # => "AB"
Методы распаковки
Методы String#unpack и String#unpack1 каждый принимают необязательный ключевой аргумент offset , который указывает смещение в строке:
'ABC'.unpack('C*', offset: 1) # => [66, 67]
'ABC'.unpack1('C*', offset: 1) # => 66
Оба метода могут принимать блок:
# Each unpacked object is passed to the block.
ret = []
"ABCD".unpack("C*") {|c| ret << c }
ret # => [65, 66, 67, 68]
# The single unpacked object is passed to the block.
'AB'.unpack1('C*') {|ele| p ele } # => 65
Директивы целых чисел
Каждая директива целого числа определяет упаковку или распаковку для одного элемента в входном или выходном массиве.
Директивы 8-битных целых чисел
-
'c'- 8-битное знаковое целое число (как Csigned char):[0, 1, 255].pack('c*') # => "\x00\x01\xFF" s = [0, 1, -1].pack('c*') # => "\x00\x01\xFF" s.unpack('c*') # => [0, 1, -1] -
'C'- 8-битное знаковое целое число (как Cunsigned char):[0, 1, 255].pack('C*') # => "\x00\x01\xFF" s = [0, 1, -1].pack('C*') # => "\x00\x01\xFF" s.unpack('C*') # => [0, 1, 255]
Директивы 16-битных целых чисел
-
's'- 16-битное знаковое целое число, родной порядок байтов (как Cint16_t):[513, -514].pack('s*') # => "\x01\x02\xFE\xFD" s = [513, 65022].pack('s*') # => "\x01\x02\xFE\xFD" s.unpack('s*') # => [513, -514] -
'S'- 16-битное беззнаковое целое число, родной порядок байтов (как Cuint16_t):[513, -514].pack('S*') # => "\x01\x02\xFE\xFD" s = [513, 65022].pack('S*') # => "\x01\x02\xFE\xFD" s.unpack('S*') # => [513, 65022] -
'n'- 16-битное сетевое целое число, порядок байтов big-endian:s = [0, 1, -1, 32767, -32768, 65535].pack('n*') # => "\x00\x00\x00\x01\xFF\xFF\x7F\xFF\x80\x00\xFF\xFF" s.unpack('n*') # => [0, 1, 65535, 32767, 32768, 65535] -
'v'- 16-битное целое число VAX, порядок байтов little-endian:s = [0, 1, -1, 32767, -32768, 65535].pack('v*') # => "\x00\x00\x01\x00\xFF\xFF\xFF\x7F\x00\x80\xFF\xFF" s.unpack('v*') # => [0, 1, 65535, 32767, 32768, 65535]
Директивы 32-битных целых чисел
-
'l'- 32-битное знаковое целое число, родной порядок байтов (как Cint32_t):s = [67305985, -50462977].pack('l*') # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" s.unpack('l*') # => [67305985, -50462977] -
'L'- 32-битное беззнаковое целое число, родной порядок байтов (как Cuint32_t):s = [67305985, 4244504319].pack('L*') # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" s.unpack('L*') # => [67305985, 4244504319] -
'N'- 32-битное сетевое целое число, порядок байтов big-endian:s = [0,1,-1].pack('N*') # => "\x00\x00\x00\x00\x00\x00\x00\x01\xFF\xFF\xFF\xFF" s.unpack('N*') # => [0, 1, 4294967295] -
'V'- 32-битное целое число VAX, порядок байтов little-endian:s = [0,1,-1].pack('V*') # => "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF" s.unpack('v*') # => [0, 0, 1, 0, 65535, 65535]
Директивы 64-битных целых чисел
-
'q'- 64-битное знаковое целое число, родной порядок байтов (как Cint64_t):s = [578437695752307201, -506097522914230529].pack('q*') # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8" s.unpack('q*') # => [578437695752307201, -506097522914230529] -
'Q'- 64-битное беззнаковое целое число, родной порядок байтов (как Cuint64_t):s = [578437695752307201, 17940646550795321087].pack('Q*') # => "\x01\x02\x03\x04\x05\x06\a\b\xFF\xFE\xFD\xFC\xFB\xFA\xF9\xF8" s.unpack('Q*') # => [578437695752307201, 17940646550795321087]
Зависимые от платформы директивы целых чисел
-
'i'- Зависимое от платформы знаковое целое число ширины, родной порядок байтов (как Cint):s = [67305985, -50462977].pack('i*') # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" s.unpack('i*') # => [67305985, -50462977] -
'I'- Зависимое от платформы беззнаковое целое число ширины, родной порядок байтов (как Cunsigned int):s = [67305985, -50462977].pack('I*') # => "\x01\x02\x03\x04\xFF\xFE\xFD\xFC" s.unpack('I*') # => [67305985, 4244504319]
Директивы указателей
-
'j'- 64-битное целое число ширины указателя, родной порядок байтов (как Cintptr_t):s = [67305985, -50462977].pack('j*') # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\xFF\xFF\xFF\xFF" s.unpack('j*') # => [67305985, -50462977] -
'j'- 64-битное беззнаковое целое число ширины указателя, родной порядок байтов (как Cuintptr_t):s = [67305985, 4244504319].pack('J*') # => "\x01\x02\x03\x04\x00\x00\x00\x00\xFF\xFE\xFD\xFC\x00\x00\x00\x00" s.unpack('J*') # => [67305985, 4244504319]
Другие директивы целых чисел
:
-
'U'- Символ UTF-8:s = [4194304].pack('U*') # => "\xF8\x90\x80\x80\x80" s.unpack('U*') # => [4194304] -
'w'- BER-кодированное целое число (см. BER-кодирование):s = [1073741823].pack('w*') # => "\x83\xFF\xFF\xFF\x7F" s.unpack('w*') # => [1073741823]
Модификаторы для директив целых чисел
Для директив в 'i', 'I', 's', 'S', 'l', 'L', 'q', 'Q', 'j', и 'J', эти модификаторы могут быть добавлены:
-
'!'или'_'- Базовая платформа родной размер. -
'>'- Big-endian. -
'<'- Little-endian.
Директивы чисел с плавающей запятой
Каждая директива числа с плавающей запятой определяет упаковку или распаковку для одного элемента в входном или выходном массиве.
Директивы чисел с плавающей запятой одинарной точности
-
'F'или'f'- Родной формат:s = [3.0].pack('F') # => "\x00\x00@@" s.unpack('F') # => [3.0] -
'e'- Little-endian:s = [3.0].pack('e') # => "\x00\x00@@" s.unpack('e') # => [3.0] -
'g'- Big-endian:s = [3.0].pack('g') # => "@@\x00\x00" s.unpack('g') # => [3.0]
Директивы чисел с плавающей запятой двойной точности
-
'D'или'd'- Родной формат:s = [3.0].pack('D') # => "\x00\x00\x00\x00\x00\x00\b@" s.unpack('D') # => [3.0] -
'E'- Little-endian:s = [3.0].pack('E') # => "\x00\x00\x00\x00\x00\x00\b@" s.unpack('E') # => [3.0] -
'G'- Big-endian:s = [3.0].pack('G') # => "@\b\x00\x00\x00\x00\x00\x00" s.unpack('G') # => [3.0]
Директива числа с плавающей запятой может быть бесконечностью или неопределенным числом:
inf = 1.0/0.0 # => Infinity
[inf].pack('f') # => "\x00\x00\x80\x7F"
"\x00\x00\x80\x7F".unpack('f') # => [Infinity]
nan = inf/inf # => NaN
[nan].pack('f') # => "\x00\x00\xC0\x7F"
"\x00\x00\xC0\x7F".unpack('f') # => [NaN]
Директивы строк
Каждая директива строки определяет упаковку или распаковку для одного байта в входной или выходной строке.
Директивы двоичных строк
-
'A'- Произвольная двоичная строка (заполнена пробелами; количество - ширина);nilобрабатывается как пустая строка:['foo'].pack('A') # => "f" ['foo'].pack('A*') # => "foo" ['foo'].pack('A2') # => "fo" ['foo'].pack('A4') # => "foo " [nil].pack('A') # => " " [nil].pack('A*') # => "" [nil].pack('A2') # => " " [nil].pack('A4') # => " " "foo\0".unpack('A') # => ["f"] "foo\0".unpack('A4') # => ["foo"] "foo\0bar".unpack('A10') # => ["foo\x00bar"] # Reads past "\0". "foo ".unpack('A') # => ["f"] "foo ".unpack('A4') # => ["foo"] "foo".unpack('A4') # => ["foo"] russian = "\u{442 435 441 442}" # => "тест" russian.size # => 4 russian.bytesize # => 8 [russian].pack('A') # => "\xD1" [russian].pack('A*') # => "\xD1\x82\xD0\xB5\xD1\x81\xD1\x82" russian.unpack('A') # => ["\xD1"] russian.unpack('A2') # => ["\xD1\x82"] russian.unpack('A4') # => ["\xD1\x82\xD0\xB5"] russian.unpack('A*') # => ["\xD1\x82\xD0\xB5\xD1\x81\xD1\x82"] -
'a'- Произвольная двоичная строка (заполнена нулями; количество - ширина):["foo"].pack('a') # => "f" ["foo"].pack('a*') # => "foo" ["foo"].pack('a2') # => "fo" ["foo\0"].pack('a4') # => "foo\x00" [nil].pack('a') # => "\x00" [nil].pack('a*') # => "" [nil].pack('a2') # => "\x00\x00" [nil].pack('a4') # => "\x00\x00\x00\x00" "foo\0".unpack('a') # => ["f"] "foo\0".unpack('a4') # => ["foo\x00"] "foo ".unpack('a4') # => ["foo "] "foo".unpack('a4') # => ["foo"] "foo\0bar".unpack('a4') # => ["foo\x00"] # Reads past "\0". -
'Z'- То же, что'a', за исключением того, что нули добавляются или игнорируются с'*':["foo"].pack('Z*') # => "foo\x00" [nil].pack('Z*') # => "\x00" "foo\0".unpack('Z*') # => ["foo"] "foo".unpack('Z*') # => ["foo"] "foo\0bar".unpack('Z*') # => ["foo"] # Does not read past "\0".
Директивы битовых строк
-
'B'- Битовая строка (старший байт сначала):['11111111' + '00000000'].pack('B*') # => "\xFF\x00" ['10000000' + '01000000'].pack('B*') # => "\x80@" ['1'].pack('B0') # => "" ['1'].pack('B1') # => "\x80" ['1'].pack('B2') # => "\x80\x00" ['1'].pack('B3') # => "\x80\x00" ['1'].pack('B4') # => "\x80\x00\x00" ['1'].pack('B5') # => "\x80\x00\x00" ['1'].pack('B6') # => "\x80\x00\x00\x00" "\xff\x00".unpack("B*") # => ["1111111100000000"] "\x01\x02".unpack("B*") # => ["0000000100000010"] "".unpack("B0") # => [""] "\x80".unpack("B1") # => ["1"] "\x80".unpack("B2") # => ["10"] "\x80".unpack("B3") # => ["100"] -
'b'- Битовая строка (младший байт сначала):['11111111' + '00000000'].pack('b*') # => "\xFF\x00" ['10000000' + '01000000'].pack('b*') # => "\x01\x02" ['1'].pack('b0') # => "" ['1'].pack('b1') # => "\x01" ['1'].pack('b2') # => "\x01\x00" ['1'].pack('b3') # => "\x01\x00" ['1'].pack('b4') # => "\x01\x00\x00" ['1'].pack('b5') # => "\x01\x00\x00" ['1'].pack('b6') # => "\x01\x00\x00\x00" "\xff\x00".unpack("b*") # => ["1111111100000000"] "\x01\x02".unpack("b*") # => ["1000000001000000"] "".unpack("b0") # => [""] "\x01".unpack("b1") # => ["1"] "\x01".unpack("b2") # => ["10"] "\x01".unpack("b3") # => ["100"]
Директивы шестнадцатеричных строк
-
'H'- Шестнадцатеричная строка (старший тетрад сначала):['10ef'].pack('H*') # => "\x10\xEF" ['10ef'].pack('H0') # => "" ['10ef'].pack('H3') # => "\x10\xE0" ['10ef'].pack('H5') # => "\x10\xEF\x00" ['fff'].pack('H3') # => "\xFF\xF0" ['fff'].pack('H4') # => "\xFF\xF0" ['fff'].pack('H5') # => "\xFF\xF0\x00" ['fff'].pack('H6') # => "\xFF\xF0\x00" ['fff'].pack('H7') # => "\xFF\xF0\x00\x00" ['fff'].pack('H8') # => "\xFF\xF0\x00\x00" "\x10\xef".unpack('H*') # => ["10ef"] "\x10\xef".unpack('H0') # => [""] "\x10\xef".unpack('H1') # => ["1"] "\x10\xef".unpack('H2') # => ["10"] "\x10\xef".unpack('H3') # => ["10e"] "\x10\xef".unpack('H4') # => ["10ef"] "\x10\xef".unpack('H5') # => ["10ef"] -
'h'- Шестнадцатеричная строка (младший тетрад сначала):['10ef'].pack('h*') # => "\x01\xFE" ['10ef'].pack('h0') # => "" ['10ef'].pack('h3') # => "\x01\x0E" ['10ef'].pack('h5') # => "\x01\xFE\x00" ['fff'].pack('h3') # => "\xFF\x0F" ['fff'].pack('h4') # => "\xFF\x0F" ['fff'].pack('h5') # => "\xFF\x0F\x00" ['fff'].pack('h6') # => "\xFF\x0F\x00" ['fff'].pack('h7') # => "\xFF\x0F\x00\x00" ['fff'].pack('h8') # => "\xFF\x0F\x00\x00" "\x01\xfe".unpack('h*') # => ["10ef"] "\x01\xfe".unpack('h0') # => [""] "\x01\xfe".unpack('h1') # => ["1"] "\x01\xfe".unpack('h2') # => ["10"] "\x01\xfe".unpack('h3') # => ["10e"] "\x01\xfe".unpack('h4') # => ["10ef"] "\x01\xfe".unpack('h5') # => ["10ef"]
Директивы строк указателей
-
'P'- Указатель на структуру (строка фиксированной длины):s = ['abc'].pack('P') # => "\xE0O\x7F\xE5\xA1\x01\x00\x00" s.unpack('P*') # => ["abc"] ".".unpack("P") # => [] ("\0" * 8).unpack("P") # => [nil] [nil].pack("P") # => "\x00\x00\x00\x00\x00\x00\x00\x00" -
'p'- Указатель на нуль-терминированную строку:s = ['abc'].pack('p') # => "(\xE4u\xE5\xA1\x01\x00\x00" s.unpack('p*') # => ["abc"] ".".unpack("p") # => [] ("\0" * 8).unpack("p") # => [nil] [nil].pack("p") # => "\x00\x00\x00\x00\x00\x00\x00\x00"
Другие директивы строк
-
'M'- Кодировка MIME с отступом; текстовый режим, но вход должен использовать LF, а выход - LF; (см. RFC 2045):["a b c\td \ne"].pack('M') # => "a b c\td =\n\ne=\n" ["\0"].pack('M') # => "=00=\n" ["a"*1023].pack('M') == ("a"*73+"=\n")*14+"a=\n" # => true ("a"*73+"=\na=\n").unpack('M') == ["a"*74] # => true (("a"*73+"=\n")*14+"a=\n").unpack('M') == ["a"*1023] # => true "a b c\td =\n\ne=\n".unpack('M') # => ["a b c\td \ne"] "=00=\n".unpack('M') # => ["\x00"] "pre=31=32=33after".unpack('M') # => ["pre123after"] "pre=\nafter".unpack('M') # => ["preafter"] "pre=\r\nafter".unpack('M') # => ["preafter"] "pre=".unpack('M') # => ["pre="] "pre=\r".unpack('M') # => ["pre=\r"] "pre=hoge".unpack('M') # => ["pre=hoge"] "pre==31after".unpack('M') # => ["pre==31after"] "pre===31after".unpack('M') # => ["pre===31after"] -
'm'-Base64закодированная строка; счетчик указывает байты входа между каждой новой строкой, округляется вниз до ближайшего кратного 3; если счетчик равен нулю, новые строки не добавляются; (см. RFC 4648):[""].pack('m') # => "" ["\0"].pack('m') # => "AA==\n" ["\0\0"].pack('m') # => "AAA=\n" ["\0\0\0"].pack('m') # => "AAAA\n" ["\377"].pack('m') # => "/w==\n" ["\377\377"].pack('m') # => "//8=\n" ["\377\377\377"].pack('m') # => "////\n" "".unpack('m') # => [""] "AA==\n".unpack('m') # => ["\x00"] "AAA=\n".unpack('m') # => ["\x00\x00"] "AAAA\n".unpack('m') # => ["\x00\x00\x00"] "/w==\n".unpack('m') # => ["\xFF"] "//8=\n".unpack('m') # => ["\xFF\xFF"] "////\n".unpack('m') # => ["\xFF\xFF\xFF"] "A\n".unpack('m') # => [""] "AA\n".unpack('m') # => ["\x00"] "AA=\n".unpack('m') # => ["\x00"] "AAA\n".unpack('m') # => ["\x00\x00"] [""].pack('m0') # => "" ["\0"].pack('m0') # => "AA==" ["\0\0"].pack('m0') # => "AAA=" ["\0\0\0"].pack('m0') # => "AAAA" ["\377"].pack('m0') # => "/w==" ["\377\377"].pack('m0') # => "//8=" ["\377\377\377"].pack('m0') # => "////" "".unpack('m0') # => [""] "AA==".unpack('m0') # => ["\x00"] "AAA=".unpack('m0') # => ["\x00\x00"] "AAAA".unpack('m0') # => ["\x00\x00\x00"] "/w==".unpack('m0') # => ["\xFF"] "//8=".unpack('m0') # => ["\xFF\xFF"] "////".unpack('m0') # => ["\xFF\xFF\xFF"] -
'u'- UU-закодированная строка:[0].pack("U") # => "\u0000" [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF" [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80" [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF"
Директивы смещения
-
'@'- Начать упаковку в заданном байтовом смещении; для упаковки, заполнение нулями, если необходимо:[1, 2].pack("C@0C") # => "\x02" [1, 2].pack("C@1C") # => "\x01\x02" [1, 2].pack("C@5C") # => "\x01\x00\x00\x00\x00\x02" "\x01\x00\x00\x02".unpack("C@3C") # => [1, 2] "\x00".unpack("@1C") # => [nil] -
'X'- Назад на байт:[0, 1, 2].pack("CCXC") # => "\x00\x02" [0, 1, 2].pack("CCX2C") # => "\x02" "\x00\x02".unpack("CCXC") # => [0, 2, 2]
Директива нулевого байта
-
'x'- Нулевой байт:[].pack("x0") # => "" [].pack("x") # => "\x00" [].pack("x8") # => "\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x02".unpack("CxC") # => [0, 2]
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.