我有一个数据库,其行由32个字符的十六进制GUID(存储为二进制)标识。我想知道如何动态地将这些字符串压缩为更短但仍然用户友好的表示...理想情况下用于共享URL。因为它们是十六进制的32个字符(当前不区分大小写)...我尝试用base64编码命中二进制表示。这使他们从32个字符到22个字符,但我不确定是否有更好的常见但直截了当。
我也在考虑创造性,因为即使表情符号现在在技术上也是URL安全的。不过,不确定这是不是一个好主意。
之前有没有人考虑过这个问题的跨平台解决方案?使用较小的子集完全生成新的ID会更好吗?
答案 0 :(得分:1)
您可以在URI中使用0-9
,a-z
,A-Z
和!$'()*+,-._~
(不包含具有特殊语法解释的字符)。这是74个字符。这比64好一点。您可以使用一个简单的方案从您的位流中提取6或7位,并使用它来选择一个允许的URI字符。
要进行编码,请从流中提取六位。如果它小于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是否具有前导或尾随零或其他可修改为压缩的模式?如果是这样,那么您可以安排编码方案,以便为这些情况使用更少的字符。
答案 1 :(得分:0)
请参阅此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好一点,因为它总共有66个字符需要2个额外字符,而base64则需要64个字符。但这可能没有多大区别。因此,根据您是否不介意浏览器兼容性,您可以将其他ascii字符添加到列表中。
例如使用:
var unreserved_characters="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈıÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´≡±‗¾¶§÷¸°¨·¹³²■";
拥有更多字符并且可以进一步缩小尺寸,并且可能适用于您的目标浏览器。