如何从字节创建 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];
您可以将字节转换为十六进制,然后用前导 0
s 填充生成的字符串...
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?的主要内容,如果未能解决你的问题,请参考以下文章