【问题标题】:Algorithm for compacting hexadecimal GUID in URL-safe way?以 URL 安全的方式压缩十六进制 GUID 的算法?
【发布时间】:2015-09-26 16:15:31
【问题描述】:

我有一个数据库,其中的行由 32 个字符的十六进制 GUID(存储为二进制)标识。我想知道如何将这些字符串动态压缩成更短但仍然对用户友好的表示形式……非常适合在共享 URL 中使用。因为它们是 32 个十六进制字符(目前不区分大小写)......我尝试使用 base64 编码来访问二进制表示。这使它们从 32 个字符增加到 22 个字符,但我不确定是否有更好的通用而简单的方法。

我也在考虑发挥创意,因为现在即使是表情符号在技术上也是 URL 安全的。不过,不确定这是否是个好主意。

以前有没有人考虑过这个问题的跨平台解决方案?完全使用较小的子集生成新 ID 会更好吗?

【问题讨论】:

  • 嗯,你有一个 16 字节的硬下限,因为每个十六进制数字描述半个字节。 22 个字符对我来说似乎并不比 16 个更糟糕,所以我会选择 base64。
  • 我觉得使用表情符号是一个非常糟糕的想法。 (1) 人们仍然希望 URL 是或至少看起来像“纯文本”,并且能够从纯文本文件中复制和粘贴它们; (2) 您可能会遇到各种浏览器错误和文件编码错误; (3) 你可能有像我一样的其他用户,他们只是觉得 emoji 很烦人。
  • 我同意 emoji 听起来很糟糕,但我对用这个获得创意的想法很感兴趣。 :)
  • Emoji 会让事情变得更糟,因为它们在 UTF-8 中每个占用四个字节。

标签: algorithm hash compression hex guid


【解决方案1】:

查看这个 Javascript 实现:

function toDigits(n, b){
    var digits = []
    while(n.isPositive()){
        digits.push(n.remainder(b).valueOf())
        n  = n.quotient(b);
    }
    return digits
}
function fromDigits(digits, b){
    n = BigInteger(0);
    for(var i=0;i<digits.length;i++){
        var d=parseInt(digits[i],b);
        n = n.multiply(b).add(d);
    }
    return n;
}
function changebase(n,from_base,to_base){
    var temp=fromDigits(n,from_base);
    return toDigits(temp,to_base);
}
var unreserved_characters="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
var number_of_unreserved_characters=unreserved_characters.length;

var guid="9ec54806c242982ca059661b6db74ab9";
var newbase=changebase(guid,16,number_of_unreserved_characters);
var newurl="";
for(var i=0;i<newbase.length;i++){
    newurl+=unreserved_characters[newbase[i]];
}

我使用了一个 BigInteger 库 http://silentmatt.com/biginteger/

此实现将十六进制转换为新的基数,即 URI 中允许的未保留字符数。这可能比 base64 好一点,因为它有 2 个额外的字符,总共 66 个字符,而 base64 中的 64 个字符。不过,这可能没有太大区别。所以根据你是否不介意浏览器兼容性,你可以在列表中添加其他 ascii 字符。

例如使用:

var unreserved_characters="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø×ƒáíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈıÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´≡±‗¾¶§÷¸°¨·¹³²■";

字符更多,尺寸更小,可能适用于您的目标浏览器。

【讨论】:

    【解决方案2】:

    您可以在 URI 中使用 0-9a-zA-Z!$'()*+,-._~(不包括具有特殊语法解释的字符)。那是74个字符。这比 64 好一点。您可以使用一个简单的方案从位流中提取 6 或 7 位,然后使用它来选择允许的 URI 字符之一。

    要进行编码,请从流中提取 6 个位。如果小于 54,则发出 74 组中的相应字符。如果大于或等于 54,则在其底部再拉一位。您现在有一个 108..127 范围内的七位数。减去 108 并加上 54 得到范围 54..73。从集合中发出该字符。

    现在每个字符的平均位数为 6*54/74 + 7*20/74 = 6.27。或每字节 1.276 个字符。然后,您的 16 字节 ID 将平均编码为 20.4 个字符。实际上更多一点,因为您必须在最后填充几个零位才能取出最后一个字符。实际平均值为 21.1303,最小值为 19,最大值为 22。

    这比尝试使用大整数进行基本转换更快、更简单,并且性能基本相同,只有 21 个字符。

    您的 16 字节 ID 是否倾向于包含前导零或尾随零,或其他可修改为压缩的模式?如果是这样,那么您可以安排编码方案以在这些情况下使用更少的字符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-17
      • 1970-01-01
      • 2010-12-31
      • 2012-07-31
      • 2020-11-11
      相关资源
      最近更新 更多