Как работают pack() и unpack() в Ruby

Зачем в Ruby нужен массив Packing? Как directive помогает в такой упаковке?

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

irb(main):003:0> n = [ 65, 66, 67 ]
=> [65, 66, 67]
irb(main):004:0> n.pack("ccc")
=> "ABC"
irb(main):005:0> n.pack("C")
=> "A"
irb(main):006:0> n.pack("CCC")
=> "ABC"
irb(main):007:0> n.pack("qqq")
=> "A\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\
x00\x00\x00"
irb(main):008:0> n.pack("QQQ")
=> "A\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\
x00\x00\x00"
irb(main):009:0> n.pack("SSS")
=> "A\x00B\x00C\x00"
irb(main):010:0> n.pack("sss")
=> "A\x00B\x00C\x00"
irb(main):011:0>

Теперь я вижу из консоли, что n.pack("SSS") and n.pack("sss");n.pack("ccc") and n.pack("CCC"); n.pack("qqq") and n.pack("QQQ") дает тот же результат. Тогда в чем отличия?

И в документах также не было приведено немного примеров того, как каждая директива работает в реальной программе. Меня также смущают приведенные ниже директивы, поскольку я не знаю, как их проверить? любой небольшой код с ними также полезен для меня:

  • S_, S!
  • S> L> Q>
  • S!< I!<
  • L_, L!

person Arup Rakshit    schedule 12.01.2013    source источник


Ответы (1)


Вы задаете вопрос об основных принципах хранения чисел в памяти компьютеров. Например, вы можете посмотреть на них, чтобы узнать больше:

http://en.wikipedia.org/wiki/Computer_number_format#Binary_Number_Representation
http://en.wikipedia.org/wiki/Signed_number_representations

В качестве примера возьмем разницу между S и s; оба используются для упаковки и распаковки 16-битных чисел, но один для целых чисел со знаком, а другой для беззнаковых. Это имеет важное значение, когда вы хотите распаковать строку обратно в исходные целые числа.

S: 16-битные числа без знака означают числа от 0 до 65535 (от 0 до (2^16-1))
s: 16-битные целые числа со знаком -32768 - 32767 (-(2^15) до (2^15) -1)) (один бит используется для знака)

Разницу можно увидеть здесь:

# S = unsigned: you cannot pack/unpack negative numbers
> [-1, 65535, 32767, 32768].pack('SSSS').unpack('SSSS')
=> [65535, 65535, 32767, 32768]   

# s = signed: you cannot pack/unpack numbers outside range -32768 - 32767
> [-1, 65535, 32767, 32768].pack('ssss').unpack('ssss')
=> [-1, -1, 32767, -32768]

Итак, вы видите, что вам нужно знать, как числа представлены в памяти компьютера, чтобы понять ваш вопрос. Числа со знаком используют один бит для представления знака, в то время как числа без знака не нуждаются в этом дополнительном бите, но тогда вы не можете представлять отрицательные числа.

Это очень простой способ представления чисел в двоичном виде в памяти компьютера.

Причина, по которой вам нужна упаковка, например, заключается в том, что вам нужно отправить числа в виде потока байтов с одного компьютера на другой (например, по сетевому соединению). Вы должны упаковать свои целые числа в байты, чтобы их можно было отправить по потоку. Другой вариант — отправить числа в виде строк; затем вы кодируете и декодируете их как строки на обоих концах вместо упаковки и распаковки.

Или, допустим, вам нужно вызвать C-функцию в системной библиотеке из Ruby. Системные библиотеки, написанные на C, работают с основными целыми числами (int, uint, long, short и т. д.) и C-структурами (struct). Вам нужно будет преобразовать ваши целые числа Ruby в системные целые числа или C-структуры перед вызовом таких системных методов. В этих случаях pack и unpack можно использовать для взаимодействия с такими методами.


Что касается дополнительных директив, то они касаются порядка байтов представления упакованной последовательности байтов. Посмотрите здесь, что означает порядок следования байтов и как это работает:

http://en.wikipedia.org/wiki/Endianness

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

# Big endian
> [34567].pack('S>').bytes.map(&:to_i)
=> [135, 7]   
# 34567 = (135 * 2^8) + 7

# Little endian
> [34567].pack('S<').bytes.map(&:to_i)
=> [7, 135]   
# 34567 = 7 + (135 * 2^8)
person Casper    schedule 12.01.2013
comment
+1 к вашему объяснению what and why - за то же большое спасибо. Но не могли бы вы дать мне немного кода для запрошенных директив, просто чтобы посмотреть, как они используются в реальной жизни. поскольку я не видел никакого кода в самом документе, поэтому не мог попробовать их в конце. так что ваша небольшая помощь может помочь мне дать хорошее начало этому! - person Arup Rakshit; 12.01.2013
comment
Вы хорошо объяснили предыдущую часть, но мой плохой я полностью в двух словах! :( - person Arup Rakshit; 12.01.2013
comment
34567 = 7 + (135 * 2 ^ 8) = (135 * 2 ^ 8) + 7. Прочитайте статьи в Википедии, на которые я дал вам ссылку, чтобы узнать. Все дело в том, как числа представлены в памяти вашего компьютера. В этом ответе мне нужно написать для вас книгу, чтобы полностью объяснить это. Вы должны сделать некоторые исследования для себя. В большинстве случаев вам никогда не понадобятся pack или unpack, если только вы не планируете заниматься низкоуровневым кодированием или разработкой сетевого протокола или чем-то подобным. - person Casper; 12.01.2013