我需要生成一个GUID并通过字符串表示来保存它。字符串表示应尽可能短,因为它将用作已经很长的URL字符串的一部分。
现在,我没有使用正常的abcd-efgh -...表示,而是使用生成的原始字节和base64编码,这导致字符串更短。
但是可以缩短它吗?
我可以失去一定程度的独特性并保留一个计数器,但扫描所有现有的密钥不是一种选择。建议?
答案 0 :(得分:13)
我使用Ascii85编码将Guid写入20个ASCII字符的数据库列。我发布了C#代码以防它有用。对于URL编码,特定字符集可能不同,但您可以选择适合您的应用程序的任何字符。它可以在这里找到:What is the most efficient way to encode an arbitrary GUID into readable ASCII (33-127)?
答案 1 :(得分:8)
当然,只需使用大于64的基数。您必须使用自定义字母对其进行编码,但您应该能够找到一些“url-safe”可打印的ASCII字符。
Base64使用8编码6位,因此16字节GUID值变为22字节编码。你可以用一两个字符减少它,但不能多。
答案 2 :(得分:2)
我不确定这是否可行,但您可以将所有生成的GUID放在表中,并在URL中仅使用表中GUID的索引。
您还可以减少guid的长度 - 例如,使用2个字节来表示自2010年以来的天数,以及4个字节表示自当天开始以来的毫秒数。只有在同一毫秒内生成的2个GUID才会发生冲突。你还可以添加2个随机字节,这将使这更好。
答案 3 :(得分:1)
你可以从另一个方向接近这个。生成尽可能短的字符串表示并将其映射到Guid。
使用定义的字母表生成密钥,如下所示:
在伪代码中:
string RandomString(char[] alphabet, int length)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < length; i++)
result.Append(alphabet[RandomInt(0, alphabet.Length)]);
return result;
}
如果你保持字符串长度&lt; 16,你可以简单地对结果进行十六进制编码并将其传递给Guid构造函数进行解析。
答案 4 :(得分:1)
我发现这个讨论很有趣:https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
基本上,您将36个字符转换为16个字节的二进制文件,但首先使用存储过程对三个时间片进行排序:
set @uuid:= uuid();
select @uuid;
+--------------------------------------+
| @uuid |
+--------------------------------------+
| 59f3ac1e-06fe-11e6-ac3c-9b18a7fcf9ed |
+--------------------------------------+
CREATE DEFINER=`root`@`localhost`
FUNCTION `ordered_uuid`(uuid BINARY(36))
RETURNS binary(16) DETERMINISTIC
RETURN UNHEX(CONCAT(SUBSTR(uuid, 15, 4),SUBSTR(uuid, 10, 4),SUBSTR(uuid, 1, 8),SUBSTR(uuid, 20, 4),SUBSTR(uuid, 25)));
select hex(ordered_uuid(@uuid));
+----------------------------------+
| hex(ordered_uuid(@uuid)) |
+----------------------------------+
| 11e606fe59f3ac1eac3c9b18a7fcf9ed |
+----------------------------------+
答案 5 :(得分:1)
(很长一段时间,但今天只是出现了同样的需求)
UUID长度为128位,由32个十六进制加4个连字符表示。 如果我们使用64(2 ^ 6)printtable ascii`s的字典,只需将32位4位(十六进制长度)转换为22位6位组。
这是UUID的缩写。相反,36个字符可以获得22个字符,而不会丢失原始位。
https://gist.github.com/tomlobato/e932818fa7eb989e645f2e64645cf7a5
class UUIDShortner
IGNORE = '-'
BASE6_SLAB = ' ' * 22
# 64 (6 bits) items dictionary
DICT = 'a'.upto('z').to_a +
'A'.upto('Z').to_a +
'0'.upto('9').to_a +
['_', '-']
def self.uuid_to_base6 uuid
uuid_bits = 0
uuid.each_char do |c|
next if c == IGNORE
uuid_bits = (uuid_bits << 4) | c.hex
end
base6 = BASE6_SLAB.dup
base6.size.times { |i|
base6[i] = DICT[uuid_bits & 0b111111]
uuid_bits >>= 6
}
base6
end
end
# Examples:
require 'securerandom'
uuid = ARGV[0] || SecureRandom.uuid
short = UUIDShortner.uuid_to_base6 uuid
puts "#{uuid}\n#{short}"
# ruby uuid_to_base6.rb
# c7e6a9e5-1fc6-4d5a-b889-4734e42b9ecc
# m75kKtZrjIRwnz8hLNQ5hd
答案 6 :(得分:0)
不是完全相同的问题,但非常非常接近 - 我使用了CRC64,Base64,你得到11个字节,CRC64已经过测试(未经证实),不能在各种字符串上产生重复。
根据定义它是64位长 - 你得到的密钥是一半大小。
要直接回答原始问题 - 您可以对您的GUID的任何表示进行CRC64编码。
或者只是在业务键上运行CRC64,然后你将拥有64位独特的东西,然后你可以使用base64。