【问题标题】:How can I shorten a UUID to a specific length?如何将 UUID 缩短到特定长度?
【发布时间】:2014-10-28 18:33:27
【问题描述】:

我想将 UUID 用于数据库记录,但如果我将它用于 URL,我希望它是 5 到 8 个字符。

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

【问题讨论】:

    标签: ruby uuid


    【解决方案1】:

    我使用 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"
    

    【讨论】:

      【解决方案2】:

      我基于 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
      

      【讨论】:

      • 如果我在我的 Postgres 数据库中存储缩短的 UUID 会有问题吗?
      • 嗨@Qasim,你可以,但我认为你必须将它存储为字符串,然后不像postgresql本机uuid类型那样高效。
      【解决方案3】:

      正如另一个答案指出的那样,您无法将真正的 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。

      【讨论】:

      • 确保你也看看下面 Luc Boissaye 的回答。
      猜你喜欢
      • 1970-01-01
      • 2012-07-14
      • 2010-11-04
      • 2020-01-08
      • 2018-12-09
      • 1970-01-01
      • 2015-03-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多