对我而言,使用UTF-8作为目标压缩字符串似乎不太合理...这看起来只会给自己找麻烦。如果数据传输大小很重要,我认为最好失去一些压缩率并使用纯7位ASCII作为目标。
如果存储限制是基于UTF-16字符,则可以寻找一个大的安全子集来进行转义或UTF-16兼容性,或者如果其他所有内容(例如数据库)没有问题,可以尝试将每个字符用作0..65535。
大多数软件层都应该没有问题(滥用),但请注意,在UTF-16范围内,0xD800-0xDFFF保留用于特殊用途(代理配对),因此某些组合在形式上是“编码错误”,理论上可能会被停止或扭曲。
在我为了好玩编写的一个玩具
4 KB JavaScript演示中,我使用了一种编码方式来存储压缩结果,将四个二进制字节存储到从ASCII的85个字符子集中选择的五个字符中(85^5略大于(2^8)^4,但仍适合JavaScript整数的精度),这使得压缩数据对于例如
JSON是安全的,无需任何转义。
代码如下,构建了85个“安全”字符列表:
let cset = "";
for (let i=35; i<35+85+1; i++) {
if (i !== 92) cset += String.fromCharCode(i);
}
将4个字节(b0
,b1
,b2
和b3
,每个字节的值在0到255之间)编码成5个字符的代码如下:
// First convert to 0...4294967295
let x = ((b0*256 + b1)*256 + b2)*256 + b3;
// Then convert to base 85
let result = "";
for (let i=0; i<5; i++) {
let x2 = Math.floor(x / 85);
result += cset[x - x2*85];
x = x2;
}
要解码,您需要进行反向计算,即从基于85的数字计算x,然后提取4个基于256的数字(即字节)。
注意:在环形代码中,我使用了略有不同的字符集,而不是跳过92个\,我用126个~代替它。对于有兴趣的人,完整的解压缩代码如下:
let Z = 5831;
let i = 0,
a = 0,
n = 0;
let b = function(){
if (!n) {
a = 0;
let w = 5;
while (w--) {
let y = s.charCodeAt(i+w);
a = a*85 + (y > 125 ? 92 : y) - 35;
}
n = 32;
i += 5;
}
return (a >> --n) & 1;
};
let v = function(z){
return (--z ? v(z) : 0)*2+b();
};
let h = function(){
return b() ? [h(), h()] : v(8);
};
let A = h(), T = h();
let d = function(n){
return n.map ? d(n[b()]) : n;
};
let S="";
while(S.length<Z){
x = d(T);
if (x < 128) {
S += String.fromCharCode(x);
} else {
S += S.substr(S.length-(d(A)<<8)-v(8), x-128)
};
}
(请注意,我没有测试这个重新格式化/注释的版本,可能存在拼写错误。)
u-lzss
似乎只能处理 UTF-8 编码的字符串。 - Frédéric Hamidi