5

250+ 1と0のJavaScript配列をもう少し扱いやすいもの(たとえば短い文字列)に圧縮してから、同じものを扱いやすく解凍する方法はありますか?Googleが画像エンコーディングを行った方法のようなものです...

ありがとう!

4

7 に答える 7

2

Base 32としてエンコードすることで、ほぼ1:5の圧縮を実現できます。可変長を許可するために、単純な長さの値を含めることにしました。値をラウンドトリップできる2つの関数を使用した手法を示すこのフィドルをご覧ください。(または、 @ slebetmanがjavascriptに存在する基数変換を思い出させる前に作成した、以前のより単純な16進バージョンを見ることができます。)

これは、250個の1と0の1セットの出力例です。文字数は先頭の「250|」をカウントしません:

base 32, 50 chars: 250|qgl6alf1q2lbl1aclau3k5ana2kpals78alek59ilboeglajgu
base 16, 63 chars: 250|D42A6555E1D0AABA854CAABC3A155750A995578742AAEA1532AAF0E85553878

Base 64エンコーディングを使用して42文字に減らすことができますが、Base32バージョンとBase64バージョンの両方で、最終結果に不快な単語が含まれる可能性があることに注意してください(上記のフィドルを参照してください。例)。16進バージョンにも不快なコンテンツが含まれている可能性がありますが、それほどではありません(悪い顔はお父さんにCADになりますか?)

さらに8文字保存する必要がある場合は、お知らせください。追加のスクリプトを作成します。母音を避けることは、不快な文章題に対処する1つの方法である可能性があります。これも行う必要がある場合はお知らせください。

ビット文字列が常に250文字である場合、関数は少し単純化できますが、私はこの仮定をしたくありませんでした。

参考までに、bits-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ビットにパディングする各変換関数の2番目のバージョンを含めました。これにより、最大2つの偽の余分な文字が生成される可能性があります。速度が重要な場合は、入力からより大きなチャンクを取得するため、高速になる場合とそうでない場合があるため、これらを含めました。

于 2012-11-16T00:32:32.430 に答える
2

(他の回答ではあまり説明がありませんでしたので、私のアプローチを提示することに加えて、これまでに提示したアプローチについて議論したいと思います。ご容赦ください。)

他の回答が示すように、ビットの配列はビットのストリームとして扱うことができます。これは基本的に、基数2で書き込まれるかなり大きな数です。同じ数を別の基数で書き込むことができます。10進数以外の1文字は、より大きな基数のより高い値の桁に使用できるため(16進数の15の場合は「F」または「f」など)、基数が大きいほど、表示に必要な桁(文字)は少なくなります。それ。

それらの回答で示唆されているように、base64エンコーディングとさらに大きなベースを使用できます(Unicode Base Multilingual Planeには65536コードポイントがあり、準拠するECMAScript実装はそれをサポートしているため、ベース65536は明確な可能性ですが、もう一度パーセントエンコードする必要がありますURIの場合)、ただしECMAScriptでは、ユーザー定義関数、おそらくそれを含むライブラリが必要になります。少なくとも、ネイティブの実装よりも必然的に遅い変換アルゴリズムの非ネイティブの実装が必要です。

幸い、ECMAScriptの実装には、基数2から36までの数値を1つの基数から別の基数に変換できるメソッドが組み込まれています。ベースで書かれparseInt(string, radix)た数値をタイプの値に変換できるものと、値をベースで書かれた数値にString変換できるものがあります。stringradixNumbernumber.toString(radix)NumbernumberStringradix

ただし、ECMAScriptNumberタイプはIEEE-754倍精度浮動小数点数の実装であるため、整数精度にはいくつかの制限があります。AIUIの1つは、1でいっぱいのビット配列の場合、配列に53を超えるビット要素が含まれていない(または文字列に53を超える「1」が含まれていない)場合を除いて、ビット文字列全体を変換して変換することはできません。精度を損なうことなく戻る。(IEEE-754 doublesの仮数は53ビットの精度です。

ただし、大きい(バイナリ)数値を小さい(バイナリ)数値文字列の連結として表示し、元のビットストリームを十分に小さいチャンクに分割し、各チャンクを大きいベースに変換することができます。いずれの場合も、0チャンクごとに失われる連続する上位ビットに関する情報。したがって、変換結果からビットストリームを復元するときは、左側の各チャンクにゼロを埋めて、デコードされた各チャンクが元のチャンクと同じ長さになるようにする必要があります。チャンクサイズは、ストリームをエンコードするために必要なステップ数と、ストリームをデコードするときにパディングする必要があるゼロの数と比較検討する必要があります。

AIUI、ビットストリームを左から右に処理する場合、各チャンクによってエンコードされる数は潜在的に大きくなります。したがって、チャンクの上位ビットが設定される可能性があるため、ベースが大きくても、エンコードされる文字列は潜在的に長くなります(たとえば、右方向11|001|001– 3 | 1 | 1 –と左方向– –を110|010|01比較し6|2|1ます。どちらも、チャンクサイズは3)です。そもそもデータをエンコードする理由は短い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のランダムビットの配列– </ p>

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);

これらの一般的な関数を使用すると、ユースケースに合わせてエンコードされた文字列を最適化するために、チャンクのサイズとベースを変更できます。(不快感を与える可能性のある単語は、区切り文字のために2つに分割されている可能性があります。)

ただし、元の配列の長さがチャンクサイズの倍数でない場合、デコードされた配列には余分な先行ゼロが含まれることに注意してください。その可能性が存在し、問題が発生する場合は、ErikEによって提案されているように、元の長さを渡し、その値を使用することで、その問題を修正できます。

var originalLength = …;

a = a.slice(a.length - originalLength);

または(バージョン1.6より前のJavaScriptおよびバージョン9.52より前のOpera ECMAScriptを除くすべての主要な実装)

a = a.slice(-originalLength);
于 2012-11-17T20:24:55.557 に答える
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;
};
于 2012-11-16T00:30:35.567 に答える
0

これは、1と0を16進数に変換する実装です。サーバーでは、1と0に戻すのはかなり簡単なはずです。16進数に変換すると、基本的に1文字あたり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;
}

明らかに、返された配列を結合して、16進文字列に変換できます。

データを8ビットアラインメントにパディングする場合、スプライスパラメータを次のように変更することにより、ループごとに8ビットを操作することにより、関数を少し高速化できます。

binArray.splice(0,8)

同様に、データを16ビットアラインメントにパディングすると、一度に16ビットをスプライスすることでデータを再び高速化できます。私が信じている制限は、浮動小数点表現のためにjavascriptが数値の丸めを開始する前の32ビットです。さまざまなjavascriptエンジンが32ビット整数の符号をどのように処理するかわからないため、最大で16を使用する方が快適です。

于 2012-11-16T01:16:03.207 に答える
0

base64を使用してみませんか?少し前にこのようなものを書きましたが、型付き配列を使用しています。

https://github.com/beatgammit/base64-js/blob/master/lib/b64.js

基本的には、1と0をバイトに変換し、base64でエンコードします。Base64はURLで渡すことができるので、状況に応じて機能します。

于 2012-11-16T01:22:46.777 に答える
0

ああ!数ヶ月前に読んだ記事をついに見つけました。文字列を効率的に圧縮するための複数の方法について説明しています。試してみてください。これがそれです。

論文で言及されている技術:

  • base64
  • latin1
  • utf-16
  • png
于 2012-11-16T05:19:41.637 に答える
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"

エスケープ/アンエスケープの代わりに、btoaatobを使用して、エンコードを短くすることもできます。

これらの関数とその使用法は、次の実例で示されています:http: //jsfiddle.net/EU4nL/

于 2012-11-16T07:33:31.887 に答える