有没有办法将250+ 1和0的JavaScript数组压缩成更易于管理的东西(比如更短的字符串)然后可以自然地解压缩?有点像谷歌的图像编码方式......
谢谢!
答案 0 :(得分:2)
我可以通过编码作为基数32给你几乎1:5的压缩。我选择包含一个简单的长度值,使其允许可变长度。请参阅this fiddle demonstrating the technique,其中包含两个允许您往返值的函数。 (或者你可以看到我在@slebetman之前创建的更早,更天真的hexadecimal version提醒我javascript中存在的本机数字库转换。)
这是一组250个1和0的示例输出。字符数不计入前导“250 |”:
base 32, 50 chars: 250|qgl6alf1q2lbl1aclau3k5ana2kpals78alek59ilboeglajgu
base 16, 63 chars: 250|D42A6555E1D0AABA854CAABC3A155750A995578742AAEA1532AAF0E85553878
您可以使用base 64编码将其缩小到42个字符,但请注意,对于基本32和base 64版本,最终结果中的单词可能会令人反感(请参阅小提琴)以上为例)。十六进制版本也可能有令人反感的内容,但更不如此(一个糟糕的面孔让爸爸成为一个cad?)
如果您需要再保存8个字符,请告诉我,我会为您编写额外的脚本。避免元音可能是处理令人反感的单词问题的一种方法。如果您还需要这样做,请告诉我。
如果你的位串总是是250个字符,那么函数可以简化一点,但我不想做这个假设。
这里参考了bit-to-base-32函数。
function bitstringEncode(bitstring) {
var i, l = bitstring.length,
retval = l.toString() + '|';
for (i = 0; i < l; i += 5) {
retval += parseInt((bitstring.substr(i, 5) + '0000').substr(0, 5), 2).toString(32);
}
return retval;
}
此函数将填充到最接近的5位,并且可能会在您提供的长度末尾生成一个虚假的额外字符。我包含了每个转换函数的第二个版本,它填充到最接近的10位,这可能会产生最多两个虚假的额外字符。我把它们包括在内,因为如果速度很重要,它们可能(或者可能不会)更快,因为它们会从输入中获取更大的块。
答案 1 :(得分:2)
(在其他答案中没有太多解释,所以除了介绍我的方法之外,我想讨论到目前为止在我的答案中提出的方法。请耐心等待。)
正如其他答案所示,可以将比特数组视为比特流,这基本上是用基数2写入的相当大的数字。相同的数字可以写在另一个数字基数中。因为十进制数字以外的单个字符可用于更大数字基数的更高值数字(例如十六进制中15的“F”或“f”),数字基数越大,显示所需的数字(字符)越少它
正如那些答案中所建议的那样,你可以使用base64编码甚至更大的基础(Unicode基本多语言平面有65536个代码点和conforming ECMAScript implementations support that,所以尽管you would have to percent-encode much again for URIs)基本65536是一个明显的可能性,但在ECMAScript中需要用户定义的函数,也许是包含它的库;至少它需要转换算法的非本机实现,这必然比本机实现慢。
幸运的是,ECMAScript实现具有内置方法,允许您将数字从一个基数转换为另一个基数,从2到36(包括2和36)。有parseInt(string, radix)
可以将基础String
中写入的数字string
值radix
转换为Number
类型的值,并且number.toString(radix)
3}}您可以将Number
值number
转换为以String
为基础的数字radix
。
但是,因为the ECMAScript Number
type is an implementation of IEEE-754 double-precision floating-point numbers,对整数精度有几个限制。 AIUI的一个是,对于一个完整的数组,除非你的数组不包含超过53个位元素(或你的字符串不包含超过53“1”),你不能转换整个位字符串和回来不失精度。 (The significand of IEEE-754 doubles has a precision of 53 bits.)
但是您可以将较大(二进制)数字视为较小(二进制)数字字符串的串联,将原始比特流分成足够小的块并将每个块转换为更大的基数。在任何情况下,关于每个块的0
的连续高位的信息都会丢失。因此,当从转换结果恢复比特流时,您需要用零填充左侧的每个块,以便每个解码的块与原始块一样长。块大小需要与编码流所需的步数和解码时需要填充的零的数量进行权衡。
AIUI,如果你从左到右处理比特流,每个块编码的数字可能会更大,因此编码的字符串可能会更长,即使有更大的基数,因为可能设置了块中的高位(例如,将右边界11|001|001
- 3 | 1 | 1 - 与左边界110|010|01
- 6|2|1
进行比较 - 两者都有块大小3)。首先对数据进行编码的原因是 short URI。因此,在编码之前完成流,您应该从右到左处理流。 (如果该数字是块大小的倍数,这种方法也消除了在编码字符串中包含原始位数的必要性。)
这些注意事项导致以下一般(为了便于阅读,未完全优化)功能:
/*
* @param bitArray : Array[Number|String]
* @param chunkSize : optional Number = 53
* @param chunkBase: optional Number = 36
* @param delim : optional String = ","
* Delimiter to use.
* @return string
*/
function bitEncode (bitArray, chunkSize, chunkBase, delim)
{
var chunkArray = [];
if (!chunkSize || chunkSize < 2 || chunkSize > 53)
{
chunkSize = 53;
}
if (!chunkBase)
{
chunkBase = 36;
}
for (var i = bitArray.length; i > 0; i -= chunkSize)
{
var index = i - chunkSize;
if (index < 0)
{
index = 0;
}
var slice = bitArray.slice(index, i);
var chunk = parseInt(slice.join(""), 2).toString(chunkBase);
chunkArray.unshift(chunk);
}
return chunkArray.join(delim);
}
/*
* @param input : String
* @param length : Number > 1
* Target length of input after left-padded with zeros
* @return string
*/
function leadingZero (input, length)
{
input = String(input);
var inputLength = input.length;
if (inputLength >= length)
{
return input;
}
var padding = [];
padding.length = length + 1 - inputLength;
return padding.join("0") + input;
}
/*
* @param s : String
* @param chunkSize : optional Number = 53
* @param chunkBase : optional Number = 36
* @param delim : optional String = ","
* @return Array[string]
*/
function bitDecode (s, chunkSize, chunkBase, delim)
{
var chunkArray = s.split(delim || ",");
var bitArray = [];
if (!chunkSize || chunkSize > 53)
{
chunkSize = 53;
}
if (!chunkBase)
{
chunkBase = 36;
}
for (var i = 0, len = chunkArray.length; i < len; ++i)
{
bitArray = bitArray.concat(
leadingZero(
parseInt(chunkArray[i], chunkBase).toString(2),
chunkSize)
.split(""));
}
return bitArray;
}
如您所见,此处的默认块大小为53位,默认基数为36.因此,250个随机位的数组 -
var a = [];
for (var i = 250; i--;)
{
a[i] = +(Math.random() < 0.5);
}
- 可能是(在53位的右边框中)
/*
"11111110110011110011000011001010101010\
11010011111010010010100110100100010011001011001010111\
00100100010000101110011010000011100010010101011100011\
11100010110110111001101110000100011101101111101111100\
10001110110100010101110010011100110110100101110010011"
*/
a.join("")
默认编码为
/* "3hou1lt6,21ewvahkfvb,ck8t6olnmr,26lbvliu2rg,1dh74lghy8j" (55 characters) */
var s = bitEncode(a)
可以这样解码:
var a = bitDecode(s);
这些常规函数应该允许您改变块大小和基数,以便为您的用例优化编码字符串。 (由于分隔符,任何可能令人反感的词都可能被分成两部分。)
但是,请注意,如果原始数组长度不是块大小的倍数,则解码后的数组将包含额外的前导零。如果存在这种可能性并且存在问题,您可以通过传递ErikE建议的原始长度来解决该问题,然后使用该值:
var originalLength = …;
a = a.slice(a.length - originalLength);
或(除了版本1.6之前的JavaScript和版本9.52之前的Opera ECMAScript之外的所有主要实现)
a = a.slice(-originalLength);
答案 2 :(得分:0)
我刚刚制作了这个非常天真的实现。
它将在"111000111"
和[['1',3],['0',3], ['1',3]]
之间进行转换(反之亦然)。
希望它适用于大二进制字符串,它应该有很多重复的字符。在最坏的情况下(01010101...
),您将使用1+7*n
个字符(n
作为输入字符串的大小。)
希望有人能提供更有效的解决方案吗?
var compress = function (input){
var output = [], current = null;
for (var t = 0; t < input.length; ++t ) {
if (current === null || current[0] !== input[t]) {
current = [input[t], 0];
output.push(current);
}
++ current[1];
}
return output;
};
var decompress = function (input) {
var output = '';
for (var t = 0; t < input.length; ++t) {
for (var u = 0; u < input[t][1]; ++u) {
output += input[t][0];
}
}
return output;
};
答案 3 :(得分:0)
这是一个将1和0转换为十六进制的实现。在服务器上将它转换回1和0应该相当简单。转换为十六进制基本上每个字符存储4位,因此它会将250位的序列转换为63个字符。
但请注意,这会以4位块的形式转换数据,因此您需要将序列填充为252位(4位对齐)或256位(8位对齐)。下面的实现不处理填充,因为我不知道你想从哪一端填充数据:
function binArray2HexArray (binArray) {
var hexArray = [];
while (binArray.length) {
hexArray.push(parseInt(binArray.splice(0,4),2).toString(16));
}
return hexArray;
}
显然,您可以加入返回的数组,将其转换为十六进制字符串。
如果将数据填充为8位对齐,则可以通过将拼接参数更改为:
,通过每个循环8位操作来加速该功能。binArray.splice(0,8)
同样,如果将数据填充到16位对齐,则可以通过一次拼接16位来再次加速。由于浮点表示,javascript开始舍入数字之前,我认为限制是32位。由于我不确定各种javascript引擎如何处理32位整数的签名,我会更满意16最大值。
答案 4 :(得分:0)
为什么不使用base64?我刚才写了这样的东西,但是它使用了类型化的数组:
https://github.com/beatgammit/base64-js/blob/master/lib/b64.js
基本上只需将1和0转换为字节,base64对其进行编码即可。 Base64可以在URL中传递,因此它适用于您的情况。
答案 5 :(得分:0)
答案 6 :(得分:0)
这两个函数都需要输入字符串:
// input size must be less then 256 characters
// first byte in returned output is length of original string
// this is used during decoding for correct padding of last 8 bits
function encodeBits(input) {
var output = String.fromCharCode(input.length);
while(1) {
output += String.fromCharCode(parseInt(input.substr(0,8),2));
input = input.substr(8);
if(input.length == 0) {
break;
}
}
return output;
}
function decodeBits(input) {
var output = "";
var bits;
var finalLength = input.charCodeAt(0);
input = input.substr(1);
while(1) {
bits = input.charCodeAt(0).toString(2);
// string must be left padded with 0's
while(bits.length < 8) {
if((bits.length+output.length) == finalLength) {
break;
}
bits = "0"+bits;
}
output += bits;
input = input.substr(1);
if(input.length == 0) {
break;
}
}
return output;
}
<强>编码
var instr = "101001110010100110010000111011111010110110001001111010110110";
var encStr = encodeBits(instr);
您可以使用转义
对输出进行编码var escapedStr = escape(encStr); // returns '%3C%A7%29%90%EF%AD%89%EB%06'
<强>解码强>
使用 unescape
进行解码var unescapedStr = unescape("%3C%A7%29%90%EF%AD%89%EB%06");
var bitStr = decodeBits(unescaped);
// bitStr now contains original input
"101001110010100110010000111011111010110110001001111010110110"
作为escape / unescape的替代方法,您还可以使用btoa和atob来减少编码。
在这个工作示例中演示了这些函数及其用法: http://jsfiddle.net/EU4nL/