Javascript 压缩字符串0';s和1';s在js中 IT生产
我目前正在制作约翰·康威的《js生活游戏》。我正在开发游戏(),我正在开发一些额外的功能,比如将你的“网格/游戏”分享给你的朋友。为此,我将网格的值(如果单元格是活的或死的)提取到一个由0和1组成的长字符串中 该字符串的长度可变,因为网格的大小并不总是相同的。例如: 网格1的长度和宽度为30=>因此字符串的长度为900 网格2的长度和宽度为50=>因此字符串的长度为2500 问题 正如您所看到的,这些0和1的字符串太长,无法复制和共享 不管我怎么努力,我似乎都不能想出一个代码来将这么长的字符串压缩成易于处理的字符串 关于如何压缩(和解压)这个有什么想法吗? 我考虑过简单地写下1x1到100x100网格大小的每个可能的网格选项,并给它们一个键/参考以用作可共享代码。用手来做这件事是疯狂的,但也许你们中的任何一个人都有办法创建一个可以做到这一点的算法Javascript 压缩字符串0';s和1';s在js中 IT生产,javascript,decode,encode,compression,Javascript,Decode,Encode,Compression,我目前正在制作约翰·康威的《js生活游戏》。我正在开发游戏(),我正在开发一些额外的功能,比如将你的“网格/游戏”分享给你的朋友。为此,我将网格的值(如果单元格是活的或死的)提取到一个由0和1组成的长字符串中 该字符串的长度可变,因为网格的大小并不总是相同的。例如: 网格1的长度和宽度为30=>因此字符串的长度为900 网格2的长度和宽度为50=>因此字符串的长度为2500 问题 正如您所看到的,这些0和1的字符串太长,无法复制和共享 不管我怎么努力,我似乎都不能想出一个代码来将这么长的字符串压
如果我们假设网格包含的0比1多得多,您可能需要尝试以下简单的压缩方案:
var bin =
'0000000000000000' +
'0000001000000000' +
'0000011100000000' +
'0000001000000000' +
'0000000000000000' +
'0000000000111000' +
'0000100000111000' +
'0000000000111000' +
'0000000000000000' +
'0000000000000000' +
'0000000010000000' +
'0000000101000000' +
'0000000010000000' +
'0000000000000000' +
'0000100000000000' +
'0000000000000000';
var packed = bin
.match(/(.{4})/g)
.map(function(x) {
return parseInt(x, 2).toString(16);
})
.join('')
.replace(/00/g, 'z')
.replace(/zz/g, 'Z');
这将生成字符串“Z02z07z02ZZ380838z38ZZz8z14z08Zz8Zz”
拆包过程的作用正好相反:
var bin = packed
.replace(/Z/g, 'zz')
.replace(/z/g, '00')
.split('')
.map(function(x) {
return ('000' + parseInt(x, 16).toString(2)).substr(-4, 4);
})
.join('');
请注意,只有当输入字符串的长度是4的倍数时,此代码才能正常工作。如果情况并非如此,则必须填充输入并裁剪输出
编辑:第二种方法
如果输入是完全随机的——0和1的数量大致相同,并且没有特定的重复模式——那么您最好将二进制字符串转换为BASE64字符串。它将大大缩短(这一次的固定压缩比约为17%),用户仍可复制/粘贴
包装:
var bin =
'1110101110100011' +
'0000101111100001' +
'1010010101011010' +
'0000110111011111' +
'1111111001010101' +
'0111000011100001' +
'1011010100110001' +
'0111111110010100' +
'0111110110100101' +
'0000111101100111' +
'1100001111011100' +
'0101011100001111' +
'0110011011001101' +
'1000110010001001' +
'1010100010000011' +
'0011110000000000';
var packed =
btoa(
bin
.match(/(.{8})/g)
.map(function(x) {
return String.fromCharCode(parseInt(x, 2));
})
.join('')
);
将产生字符串“66ML4aVaDd/+VXDhtTF/LH2LD2FD3FCPZS2MAIDPAA=”
拆包:
var bin =
atob(packed)
.split('')
.map(function(x) {
return ('0000000' + x.charCodeAt(0).toString(2)).substr(-8, 8);
})
.join('');
或者如果你想更进一步,你可以考虑使用类似的东西来减少编码开销。
LZ字符串 使用我能够将“代码”压缩很多。只需将其压缩到base64,如下所示:
var compressed=LZString.compressToBase64(字符串)
解压缩也与此一样简单:
var decompressed=LZString.decompressFromBase64(已压缩)
但是,考虑到0和1的数量(示例中未给出),此压缩字符串的长度仍然相当长
但是压缩确实有效。如果还不明显,那么您试图存储的字符串看起来像一个二进制字符串 计数系统 二进制是二进制中的一个数字。这本质上意味着有两个字符用于计数。通常我们用(十进制字符)计数。在计算机科学中,十六进制()也被广泛使用 由于您不是将位存储为位而是将位存储为字节(如果您希望将其存储为位,请使用
var a=0b1100001;
),因此您希望存储的“二进制”占用的空间与具有相同长度的任何其他随机字符串一样大
由于您使用的是二进制系统,每个位置只有2个可能的值。使用十六进制值时,单个位置最多可容纳16个可能的值。在紧凑地存储数据方面,这已经是一个很大的改进。例如0b1111111
和0xff
都表示十进制数255
在您的情况下,每存储8个字节中就有6个字节。最后,你会被一根只有原来长度1/4的绳子卡住
Javascript实现
本质上,我们要做的是将存储的字符串解释为二进制并检索十六进制值。幸运的是,JavaScript具有内置功能,可以实现以下功能:
var bin =
'1110101110100011' +
'0000101111100001' +
'1010010101011010' +
'0000110111011111' +
'1111111001010101' +
'0111000011100001' +
'1011010100110001' +
'0111111110010100' +
'0111110110100101' +
'0000111101100111' +
'1100001111011100' +
'0101011100001111' +
'0110011011001101' +
'1000110010001001' +
'1010100010000011' +
'0011110000000000';
var returnValue = '';
for (var i = 0; i < parseInt(bin.length / 8); i++) {
returnValue += parseInt(bin.substr(i*8, 8), 2).toString(16);
}
console.log(bin.length); // Will return 265
console.log(returnValue.length); // Will return 64
解码时,应添加前导零,直到获得完整字节(8位)
例如,如果必须存储的位置不能除以8,则可以添加填充,并在输出字符串的前面添加一个数字,以标识要剥离的位置
等等,还有更多
要获得更短的字符串,可以构建一个包含265个字符的查找表,在其中搜索与特定位置关联的字符。(这是可行的,因为您仍然将十六进制值存储为字符串。)遗憾的是,或编码都不适合这种情况,因为有些块的值没有定义字符
它可能看起来像:
// Go fill this array until you have 265 values within it.
var lookup = ['A', 'B', 'C', 'D'];
var smallerValue = lookup[0x00];
通过这种方式,您可以在一个位置上有265个可能的值,并且您已经最大限度地使用了字节
请注意,这里没有发生真正的压缩。我们更倾向于利用数据类型,以便更有效地用于您当前的用例。答案 对于任何想知道我到底是如何做到这一点的人来说,下面是如何做到的: 首先,我确保传入的每个字符串都将填充前导0,直到它可以被8分隔。(节省用于填充的0的数量,因为解压缩时需要这些0) 我使用Corstian的答案和函数将字符串(解释为二进制)压缩为十六进制字符串。虽然我不得不做一点小小的改动 并非每个长度为8的二进制子字符串都会返回2个十六进制字符。所以对于这些情况,我只是在子字符串前面加了一个0。这个
// Go fill this array until you have 265 values within it.
var lookup = ['A', 'B', 'C', 'D'];
var smallerValue = lookup[0x00];
/**
* Compresses the a binary string into a compressed string.
* Returns the compressed string.
*/
Codes.compress = function(bin) {
bin = bin.toString(); // To make sure the binary is a string;
var returnValue = ''; // Empty string to add our data to later on.
// If the lenght of the binary string is not devidable by 8 the compression
// won't work correctly. So we add leading 0s to the string and store the amount
// of leading 0s in a variable.
// Determining the amount of 'padding' needed.
var padding = ((Math.ceil(bin.length/8))*8)-bin.length;
// Adding the leading 0s to the binary string.
for (var i = 0; i < padding; i++) {
bin = '0'+bin;
}
for (var i = 0; i < parseInt(bin.length / 8); i++) {
// Determining the substring.
var substring = bin.substr(i*8, 8)
// Determining the hexValue of this binary substring.
var hexValue = parseInt(substring, 2).toString(16);
// Not all binary values produce two hex numbers. For example:
// '00000011' gives just a '3' while what we wand would be '03'. So we add a 0 in front.
if(hexValue.length == 1) hexValue = '0'+hexValue;
// Adding this hexValue to the end string which we will return.
returnValue += hexValue;
}
// Compressing the hex string even further.
// If there's any double hex chars in the string it will take those and compress those into 1 char.
// Then if we have multiple of those chars these are compressed into 1 char again.
// For example: the hex string "ff will result in a "v" and "ffff" will result in a "V".
// Also: "11" will result in a "h" and "1111" will result in a "H"
// For the 0s this process is repeated a few times.
// (string with 4096 0s) (this would represent a 64*64 EMPTY grid)
// will result in a "Z".
var returnValue = returnValue.replace(/00/g, 'g')
.replace(/gg/g, 'G')
// Since 0s are probably more likely to exist in our binary and hex, we go a step further compressing them like this:
.replace(/GG/g, 'w')
.replace(/ww/g, 'W')
.replace(/WW/g, 'x')
.replace(/xx/g, 'X')
.replace(/XX/g, 'y')
.replace(/yy/g, 'Y')
.replace(/YY/g, 'z')
.replace(/zz/g, 'Z')
//Rest of the chars...
.replace(/11/g, 'h')
.replace(/hh/g, 'H')
.replace(/22/g, 'i')
.replace(/ii/g, 'I')
.replace(/33/g, 'j')
.replace(/jj/g, 'J')
.replace(/44/g, 'k')
.replace(/kk/g, 'K')
.replace(/55/g, 'l')
.replace(/ll/g, 'L')
.replace(/66/g, 'm')
.replace(/mm/g, 'M')
.replace(/77/g, 'n')
.replace(/nn/g, 'N')
.replace(/88/g, 'o')
.replace(/oo/g, 'O')
.replace(/99/g, 'p')
.replace(/pp/g, 'P')
.replace(/aa/g, 'q')
.replace(/qq/g, 'Q')
.replace(/bb/g, 'r')
.replace(/rr/g, 'R')
.replace(/cc/g, 's')
.replace(/ss/g, 'S')
.replace(/dd/g, 't')
.replace(/tt/g, 'T')
.replace(/ee/g, 'u')
.replace(/uu/g, 'U')
.replace(/ff/g, 'v')
.replace(/vv/g, 'V');
// Adding the number of leading 0s that need to be ignored when decompressing to the string.
returnValue = padding+'-'+returnValue;
// Returning the compressed string.
return returnValue;
}
/**
* Decompresses the compressed string back into a binary string.
* Returns the decompressed string.
*/
Codes.decompress = function(compressed) {
var returnValue = ''; // Empty string to add our data to later on.
// Splitting the input on '-' to seperate the number of paddin 0s and the actual hex code.
var compressedArr = compressed.split('-');
var paddingAmount = compressedArr[0]; // Setting a variable equal to the amount of leading 0s used while compressing.
compressed = compressedArr[1]; // Setting the compressed variable to the actual hex code.
// Decompressing further compressed characters.
compressed = compressed// Decompressing the further compressed 0s. (even further then the rest of the chars.)
.replace(/Z/g, 'zz')
.replace(/z/g, 'YY')
.replace(/Y/g, 'yy')
.replace(/y/g, 'XX')
.replace(/X/g, 'xx')
.replace(/x/g, 'WW')
.replace(/W/g, 'ww')
.replace(/w/g, 'GG')
.replace(/G/g, 'gg')
.replace(/g/g, '00')
// Rest of chars...
.replace(/H/g, 'hh')
.replace(/h/g, '11')
.replace(/I/g, 'ii')
.replace(/i/g, '22')
.replace(/J/g, 'jj')
.replace(/j/g, '33')
.replace(/K/g, 'kk')
.replace(/k/g, '44')
.replace(/L/g, 'll')
.replace(/l/g, '55')
.replace(/M/g, 'mm')
.replace(/m/g, '66')
.replace(/N/g, 'nn')
.replace(/n/g, '77')
.replace(/O/g, 'oo')
.replace(/o/g, '88')
.replace(/P/g, 'pp')
.replace(/p/g, '99')
.replace(/Q/g, 'qq')
.replace(/q/g, 'aa')
.replace(/R/g, 'rr')
.replace(/r/g, 'bb')
.replace(/S/g, 'ss')
.replace(/s/g, 'cc')
.replace(/T/g, 'tt')
.replace(/t/g, 'dd')
.replace(/U/g, 'uu')
.replace(/u/g, 'ee')
.replace(/V/g, 'vv')
.replace(/v/g, 'ff');
for (var i = 0; i < parseInt(compressed.length / 2); i++) {
// Determining the substring.
var substring = compressed.substr(i*2, 2);
// Determining the binValue of this hex substring.
var binValue = parseInt(substring, 16).toString(2);
// If the length of the binary value is not equal to 8 we add leading 0s (js deletes the leading 0s)
// For instance the binary number 00011110 is equal to the hex number 1e,
// but simply running the code above will return 11110. So we have to add the leading 0s back.
if (binValue.length != 8) {
// Determining how many 0s to add:
var diffrence = 8 - binValue.length;
// Adding the 0s:
for (var j = 0; j < diffrence; j++) {
binValue = '0'+binValue;
}
}
// Adding the binValue to the end string which we will return.
returnValue += binValue
}
var decompressedArr = returnValue.split('');
returnValue = ''; // Emptying the return variable.
// Deleting the not needed leading 0s used as padding.
for (var i = paddingAmount; i < decompressedArr.length; i++) {
returnValue += decompressedArr[i];
}
// Returning the decompressed string.
return returnValue;
}