如何将UUID缩短到特定长度?

时间:2014-10-28 18:33:28

标签: ruby uuid

我想将UU​​ID用于数据库记录,但如果我将其用于URL,我希望它为5到8个字符。

我知道我需要使用SecureRandombase64,但如何指定我需要的长度?

4 个答案:

答案 0 :(得分:7)

另一个答案指出,你不能将真正的UUID降到5-8个字符,但你可以稍微缩短它们。 UUID是128位整数,可以处理32个十六进制数字。您可以轻松地为每个字符存储6位,并将长度减少到22个字符,这是base 64编码的基础。标准base 64编码使用大写和小写字母,数字以及“+”和“/”来完成它。如果用“ - ”和“_”替换“+”和“/”,最终会得到一个不必进行url编码的字符串。您可以这样做(使用UUIDTools创建UUID):

uuid = UUIDTools::UUID.random_create
str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)

为了让你的价值回归:

(str + "==\n").tr('-_','+/').unpack('m*').first if str =~ /^[a-zA-Z0-9_\-]{22}$/

假设UUID可以放入原始格式,其中包含16个8位字符的字符串。这是一个irb会话,展示了一个真实的例子:

2.1.1 :016 > uuid=UUIDTools::UUID.random_create
 => #<UUID:0x83f1e98c UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 
2.1.1 :017 > uuid.raw
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :018 > str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)
 => "INB7bFKvTlOv6ms60NDGJw" 
2.1.1 :019 > uuid2 =  (str + "==\n").tr('-_','+/').unpack('m*').first
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :022 > UUIDTools::UUID.parse_raw(uuid2)
 => #<UUID:0x849e6b44 UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 

我在各种网站上使用此方法,我通常使用Postgres生成UUID作为表的主键并将它们作为ID传递。它不会节省大量空间,但它确实使一些URL适合于一个80字符行,其中标准格式的完整UUID不会。使用破折号时,标准UUID为36个字符,因此22个大小约为2/3。

答案 1 :(得分:2)

您无法缩短UUID。

来自Wikipedia

  

UUID是一个16字节(128位)的数字。

     

在其规范形式中,UUID由32个小写十六进制数字表示。

如果你做得更短,那就不再是UUID了。

有各种创建较短网址的技巧。只需搜索&#34;网址缩短程序&#34;并且你会发现任意数量的宝石。

答案 2 :(得分:1)

我创建了两个基于Michael Chaney的单行函数

def encode(uuid)     
  [uuid.tr('-', '').scan(/../).map(&:hex).pack('c*')].pack('m*').tr('+/', '-_').slice(0..21)                                                   
end
def decode(short_id)
 (short_id.tr('-_', '+/') + '==').unpack('m0').first.unpack('H8H4H4H4H12').join('-')                                                                                                                                                                                                                                                           
end

uuid = "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                                                                            
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                          
encode(uuid)      
=> "NVv1Af_qT1qp4hYHTeb88g"        
decode(_)         
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2

答案 3 :(得分:0)

我使用62个(Base62)字符的字母,因为我不想在缩短的UUID中使用-_。 UUID缩短器将36个长UUID缩短为22个字符长的字符串。我在Rails中使用此UUID缩短器。根据您的需要更改字母。

这是我的UUID缩短器:

代码

# lib/uuid_shortener.rb
module UuidShortener
  ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
  ALPHABET_HASH = ALPHABET.each_char.with_index.each_with_object({}) { |(k, v), h| h[k] = v; }
  BASE = ALPHABET.length

  class << self
    def shorten(uuid)
      num = uuid.tr('-', '').to_i(16)
      return '0' if num.zero?
      return nil if num.negative?

      str = ''
      while num.positive?
        str = ALPHABET[num % BASE] + str
        num /= BASE
      end
      str
    end

    def expand(suid)
      num = i = 0
      len = suid.length - 1
      while i < suid.length
        pow = BASE**(len - i)
        num += ALPHABET_HASH[suid[i]] * pow
        i += 1
      end
      num.to_s(16).rjust(32, '0').unpack('A8A4A4A4A12').join('-')
    end
  end
end

用法

  1. 生成UUID
> uuid = SecureRandom.uuid
> uuid 
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"
  1. 短UUID
> suid = UuidShortener.shorten(uuid)
> suid
=> "1svPFI0god7vT7MNxKIrfR"
  1. 展开SUID
> UuidShortener.expand(suid)
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"