如何在Ruby中将十六进制转换为二进制(反之亦然),同时保持前导零?

时间:2015-04-24 19:04:15

标签: ruby binary hex

我有一个数据结构,我想在Ruby中从十六进制转换为二进制。二进制到十六进制的最简单方法是' 0010' .to_i(2).to_s(16) - 遗憾的是,这不会保留前导零(由于to_i调用),因为人们可能需要数据结构像加密密钥(也随着前导零的数量而变化)。

是否有一种简单的内置方法可以做到这一点?

3 个答案:

答案 0 :(得分:2)

我认为您应该对加密密钥中有多少位有一个明确的想法。这应存储在程序中的某个常量或变量中,而不是存储在表示键的单个字符串中:

KEY_BITS = 16

表示密钥的最自然的方式是整数,所以如果你收到一个十六进制格式的密钥,你可以像这样转换它(字符串中的前导零并不重要):

key = 'a0a0'.to_i(16)

如果您收到(ASCII)二进制格式的密钥,您可以像这样转换它(字符串中的前导零无关紧要):

key = '101011'.to_i(2)

如果您需要以十六进制输出具有正确数量的前导零的键:

key.to_s(16).rjust((KEY_BITS+3)/4, '0')

如果您需要输出具有正确数量的前导零的二进制密钥:

key.to_s(2).rjust(KEY_BITS, '0')

如果你真的想知道基于(ASCII)二进制或十六进制字符串的密钥中可能有多少位,你可以这样做:

key_bits = binary_str.length
key_bits = hex_str.length * 4

答案 1 :(得分:2)

事实是,前导零不是整数值的一部分。我的意思是,这是与该值的表示相关的一些细节,而不是值本身。因此,如果要保留表示的属性,最好不要获取基础值。

幸运的是,十六进制二进制转换具有一个简洁属性:每个十六进制数字精确对应于4个二进制数字。因此,假设您只获得具有可被4整除的位数的二进制数,您可以构建两个用于来回构造的字典:

# Hexadecimal part is easy
hex = [*'0'..'9', *'A'..'F']
# Binary... not much longer, but a bit trickier
bin = (0..15).map { |i| '%04b' % i }

请注意使用String#%运算符,该运算符将给定值格式化,将字符串解释为printf - 样式格式字符串。

好的,所以这些是“数字”列表,每个16个。现在为词典:

hex2bin = Hash[*hex.zip(bin).flatten]
bin2hex = Hash[*bin.zip(hex).flatten]

这里感兴趣的是Hash#[]构造函数,它从2个连续值中产生键值对。实际上,你可以完全跳过“数字列表”部分并以另一种方式构造哈希,这只是我扭曲思维的第一条道路。

使用这些将十六进制转换为bin非常简单:

"DEADBEEF".each_char.map { |d| hex2bin[d] }.join

转换回来并不是那么简单。我假设我们有一个“好数字”,可以分成4个二进制数字组。我没有找到比使用String#scan“匹配每4个字符”正则表达式更简洁的方法:

"10111110".scan(/.{4}/).map { |d| bin2hex[d] }.join

程序大致相似。

奖励任务:实现相同的转换,无视我只有“好二进制数”的假设,i。即“110101”。

“I-should-have-the-docs”评论:有Hash#invert返回散列所有键值对的哈希。

答案 2 :(得分:0)

这是我发现的最简单的解决方案,可以保留前导零。要将十六进制转换为二进制:

['DEADBEEF'].pack('H*').unpack('B*').first # => "11011110101011011011111011101111"

从二进制到十六进制:

['11011110101011011011111011101111'].pack('B*').unpack1('H*') # => "deadbeef"

在这里您可以找到更多信息: