【问题标题】:How do I convert hex to binary (and vice versa) in Ruby, WHILE maintaining leading zeroes?如何在 Ruby 中将十六进制转换为二进制(反之亦然),同时保持前导零?
【发布时间】:2015-04-24 19:04:15
【问题描述】:

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

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

【问题讨论】:

  • 一般来说,加密密钥只是整数(或整数对),而在 Ruby 中表达它们最自然的方式是使用内置的 Integer 类。另外,您还应该根据它们可以容纳多少位来跟踪这些整数的上限,并且可能在将这些整数与各种 ASCII 或二进制格式相互转换时使用它。尝试将您的键存储为带有前导零的字符串,其中前导零实际上对您的程序产生影响,这似乎是个坏主意。

标签: ruby binary hex


【解决方案1】:

我认为您应该清楚地知道您的加密密钥中有多少位。这应该存储在程序中的某个常量或变量中,而不是存储在代表键的单个字符串中:

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

【讨论】:

  • 你不是大卫·格雷森!大卫看起来一点也不像。一方面,他总是戴着太阳镜。
  • 你也可以用String#%"%0#{KEY_BITS}b" % 42 #=> "0000000000101010"。 (格式见Kernel#sprintf。)
【解决方案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 = hex.zip(bin).to_h
bin2hex = bin.zip(hex).to_h

使用这些将十六进制转换为二进制很简单:

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

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

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

过程大体相似。

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

“I-should-have-read-the-docs”备注:有 Hash#invert 返回所有键值对反转的哈希。

【讨论】:

  • 很奇怪 Ruby 没有更好的方法来进行这种转换。它看起来对我来说太冗长了。
【解决方案3】:

这是我发现的最直接的解决方案,它保留了前导零。十六进制转二进制:

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

以及从二进制到十六进制:

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

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

【讨论】:

    猜你喜欢
    • 2019-03-31
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 2021-07-17
    • 2021-08-15
    • 2015-11-18
    • 2010-09-17
    • 2015-03-09
    相关资源
    最近更新 更多