如何从字节创建 ruby​​ uuid?

Posted

技术标签:

【中文标题】如何从字节创建 ruby​​ uuid?【英文标题】:How to create ruby uuid from bytes? 【发布时间】:2018-08-10 13:38:26 【问题描述】:

C# 包含从字节生成 Guid 的方法:

byte[] bytes = 107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223;
Guid guid = new Guid(bytes); // => f29b616b-3424-57b6-43df-a3a607af7bdf

如何编写 ruby​​ 代码来生成与 C# 相同的 uuid? Ruby:SecureRandom 中定义的 uuid 不接受任何参数。

【问题讨论】:

“其他系统,特别是 Microsoft 在其 COM/OLE 库中编组 UUID 的系统,使用混合端格式,其中 UUID 的前三个组件是小端,the last two are big-endian。”你在开玩笑吗,微软? 【参考方案1】:

有时开发过程涉及编写代码,而不仅仅是调用现有的库:

bytes.each_with_object([]) do |b, acc|
  acc << [] if acc.size == 0 ||
               acc.size == 1 && acc.last.size == 4 ||
               acc.size > 1 && acc.size < 5 && acc.last.size == 2
  acc.last << b.to_s(16).rjust(2, '0')
end.map.with_index do |e, idx|
  idx < 3 ? e.reverse : e
end.map(&:join).join('-')
#⇒ "f29b616b-3424-57b6-43df-a3a607af7bdf"

【讨论】:

看起来这东西应该在图书馆里:) @SergioTulentsev 我不同意。基于给定字节生成 UUID 破坏了 UUID 的整个想法。这就像 rand(bytes)bytes.pack('c*') 一样实现。 不,它不会破坏,一点也不会。这是两个同样有效的用例:生成和反序列化。如果 ruby​​ 在 stdlib 中有一个 uuid/guid type,你敢打赌它会包含这个功能。 :)【参考方案2】:

这是一种方法,只使用sprintf。我不确定我是喜欢它还是讨厌它。

arr = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]

fmt = "%4$02x%3$02x%2$02x%1$02x-" \
      "%6$02x%5$02x-%8$02x%7$02x-%9$02x%10$02x-" \
      "%11$02x%12$02x%13$02x%14$x%15$x%16$x"

str = sprintf(fmt, *arr)
# => "f29b616b-3424-57b6-43df-a3a607af7bdf"

这使用sprintf$ 标志来明确指定十六进制数字的顺序,例如%4$02x 表示“将参数中的第四个八位字节打印为两个十六进制数字。”

我们当然可以生成格式字符串:

positions = [[4, 3, 2, 1], [6, 5], [8, 7], [9, 10], 11..16]
fmt = positions.map |a| a.map |d| "%#d$02x" .join .join("-")
# => "%4$02x%3$02x%2$02x%1$02x-%6$02x%5$02x-%8$02x%7$02x-%9$02x%10$02x-%11$02x%12$02x%13$02x%14$02x%15$02x%16$02x"

str = sprintf(fmt, *arr)
# => "f29b616b-3424-57b6-43df-a3a607af7bdf"

...但此时你不妨这样做:

positions = [ [ 3, 2, 1, 0 ], [ 5, 4 ], [ 7, 6 ], [ 8, 9 ], 10..15 ]
str = positions.map |a| a.map |n| "%02x" % arr[n] .join .join("-")
# => f29b616b-3424-57b6-43df-a3a607af7bdf

您可以在 repl.it 上查看所有这些操作:https://repl.it/@jrunning/FamousRewardingApplescript

【讨论】:

【参考方案3】:

另一种给猫剥皮的方法,简单易懂:

a = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]

def _guid(ints, reverse=false)
  hexes = ints.map  |b| b.to_s(16).rjust(2, '0') 
  return hexes.reverse.join if reverse
  hexes.join
end

def guid(ints)
  '%s-%s-%s-%s-%s' % [
    _guid(ints[0...4], true),
    _guid(ints[4...6], true),
    _guid(ints[6...8], true),
    _guid(ints[8...10]),
    _guid(ints[10..-1]),
  ]
end

puts guid a # => f29b616b-3424-57b6-43df-a3a607af7bdf

【讨论】:

在函数中定义函数不是 Ruby 的惯用语。您不妨将_guid 定义在与guid 相同的级别,因为无论如何它都会在该范围内定义。【参考方案4】:

这可以通过将字节转换为十六进制字符串数组,在正确的位置插入-,然后加入数组来以非常简单的方式完成。

def to_uuid(bytes)
  hex = bytes.map  |b| b.to_s(16).rjust(2, '0') 
  [4, 7, 10, 13].inject(hex)  |hex, n| hex.insert(n, '-') .join
end

to_uuid([107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223])
# => "6b619bf2-2434-b657-43df-a3a607af7bdf"

给定一个字节数组...

bytes = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223];

您可以将字节转换为十六进制,然后用前导 0s 填充生成的字符串...

hex = bytes.map  |b| b.to_s(16).rjust(2, '0') 
# => ["6b", "61", "9b", "f2", "24", "34", "b6", "57", "43", "df", "a3", "a6", "07", "af", "7b", "df"]

然后在正确的位置插入连字符...

[4, 7, 10, 13].inject(hex)  |hex, n| hex.insert(n, '-') 
# => ["6b", "61", "9b", "f2", "-", "24", "34", "-", "b6", "57", "-", "43", "df", "-", "a3", "a6", "07", "af", "7b", "df"]

然后加入数组:

hex.join
# => "6b619bf2-2434-b657-43df-a3a607af7bdf"

【讨论】:

【参考方案5】:

第一个近似答案:

a = [107, 97, 155, 242, 36, 52, 182, 87, 67, 223, 163, 166, 7, 175, 123, 223]
ah = a.map |i| i.to_s(16) 

puts [4,2,2,2,6].inject([])  |result, idx| result << ah.slice!(0, idx).reverse.join .join("-")

f29b616b-3424-57b6-df43-df7baf7a6a3
=> nil

几乎可以肯定有一种更简洁的方法来实现,但这会给你一些东西可以使用。它使用注入将生成的 uuid 字符串部分累积到一个数组中,然后将它们连接到 guid 中。

guid 的每个块都是字节数组的一个子数组,看似有序的 lsb 到 msb。

【讨论】:

最后两部分将被颠倒以符合OP提供的结果。 谢谢,我没有注意到这种不足。不过,我暂时保持原样。 顺便说一句,map 有什么问题,因为您已经发明了智能初始数组? [4,2,2,2,6].map |idx| ah.slice!(0, idx).reverse.join .join('-') 的结果与您的相同。 @mudasobwa map 没什么问题 - 我实际上正在使用它,然后想'哦,让我们看看我是否可以用注入来代替':-)

以上是关于如何从字节创建 ruby​​ uuid?的主要内容,如果未能解决你的问题,请参考以下文章

从字节创建 zip 文件

Ruby - 肥皂和字节数组的摘要认证

如何在 Ruby 中删除 4 字节 utf-8 字符?

如何从 Ruby 中的 CSV 标题行创建变量

如何从 NodeJS 中的用户名中获取 Minecraft 玩家的 UUID?

如何从哈希数组创建 ruby​​ 类的实例?