Данные в упакованном формате
Некоторые методы ядра 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'- Целое число размера указателя, знаковое, порядок байтов по умолчанию (как 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'- Целое число размера указателя, беззнаковое, порядок байтов по умолчанию (как 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'- Cint, всегда размер платформы по умолчанию. -
's','S'- Cshort. -
'l','L'- Clong. -
'q','Q'- Clong long, если доступно. -
'j','J'- Cintptr_t, всегда размер платформы по умолчанию.
Модификаторы размера платформы игнорируются для директив, всегда использующих размер платформы по умолчанию.
Также к вышеперечисленным директивам можно добавить модификаторы порядка байтов:
-
'>'- 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'- Кодировка Quoted printable, 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.