C#压缩一个字节数组

时间:2010-07-17 02:08:34

标签: c# compression xbox360

我对压缩算法知之甚少。我正在寻找一个简单的压缩算法(或代码片段),它可以减少字节[,,]或字节[]的大小。我无法使用System.IO.Compression。此外,数据有很多重复。

我尝试实施RLE算法(下面张贴供您检查)。但是,它产生的阵列大1.2到1.8倍。

public static class RLE
{
    public static byte[] Encode(byte[] source)
    {
        List<byte> dest = new List<byte>();
        byte runLength;

        for (int i = 0; i < source.Length; i++)
        {
            runLength = 1;
            while (runLength < byte.MaxValue 
                && i + 1 < source.Length 
                && source[i] == source[i + 1])
            {
                runLength++;
                i++;
            }
            dest.Add(runLength);
            dest.Add(source[i]);
        }

        return dest.ToArray();
    }

    public static byte[] Decode(byte[] source)
    {
        List<byte> dest = new List<byte>();
        byte runLength; 

        for (int i = 1; i < source.Length; i+=2)
        {
            runLength = source[i - 1];

            while (runLength > 0)
            {
                dest.Add(source[i]);
                runLength--;
            }
        }
        return dest.ToArray();
    }

}

我还发现了一个基于java,字符串和整数的LZW实现。我已将其转换为C#,结果看起来很好(下面的代码发布)。但是,我不确定它是如何工作的,也不知道如何使用字节而不是字符串和整数。

public class LZW
{
    /* Compress a string to a list of output symbols. */
    public static int[] compress(string uncompressed)
    {
        // Build the dictionary.
        int dictSize = 256;
        Dictionary<string, int> dictionary = new Dictionary<string, int>();
        for (int i = 0; i < dictSize; i++)
            dictionary.Add("" + (char)i, i);

        string w = "";
        List<int> result = new List<int>();

        for (int i = 0; i < uncompressed.Length; i++)
        {
            char c = uncompressed[i];
            string wc = w + c;
            if (dictionary.ContainsKey(wc))
                w = wc;
            else
            {
                result.Add(dictionary[w]);
                // Add wc to the dictionary.
                dictionary.Add(wc, dictSize++);
                w = "" + c;
            }
        }

        // Output the code for w.
        if (w != "")
            result.Add(dictionary[w]);
        return result.ToArray();
    }

    /* Decompress a list of output ks to a string. */
    public static string decompress(int[] compressed)
    {
        int dictSize = 256;
        Dictionary<int, string> dictionary = new Dictionary<int, string>();
        for (int i = 0; i < dictSize; i++)
            dictionary.Add(i, "" + (char)i);

        string w = "" + (char)compressed[0];
        string result = w;
        for (int i = 1; i < compressed.Length; i++)
        {
            int k = compressed[i];
            string entry = "";
            if (dictionary.ContainsKey(k))
                entry = dictionary[k];
            else if (k == dictSize)
                entry = w + w[0];

            result += entry;

            // Add w+entry[0] to the dictionary.
            dictionary.Add(dictSize++, w + entry[0]);

            w = entry;
        }

        return result;
    }
}

3 个答案:

答案 0 :(得分:1)

看看here。我使用此代码作为压缩我的一个工作项目的基础。不确定在Xbox 360 SDK中有多少.NET Framework是可访问的,所以不确定这对你有多好。

答案 1 :(得分:0)

查看霍夫曼代码,这是一个非常简单的算法。基本上,对于更频繁出现的模式使用较少的位,并保留表格的编码方式。而且你必须在你的代码字中考虑到没有分隔符可以帮助你解码。

答案 2 :(得分:0)

RLE算法的问题在于它太简单了。它为每个字节前缀重复多少次,但这确实意味着在非重复字节的长范围内,每个字节都以&#34; 1&#34;为前缀。对于没有重复的数据,这将加倍文件大小。

这可以通过使用代码类型的RLE来避免; &#39;代码&#39; (也叫做#Token&#39;)将是一个可以有两个含义的字节;或者它指示单个后续字节重复的次数,或者它指示应该按原样复制的非重复字节数。这两个代码之间的差异是通过启用最高位来实现的,这意味着该值仍有7位可用,这意味着每个此类代码的复制或重复量最多可达127位。

这意味着即使在最坏的情况下,最终大小也只能比原始文件大小大约1/127。

对整个概念的一个很好的解释,加上完整的工作(事实上,经过大量优化的)C#代码,可以在这里找到:

http://www.shikadi.net/moddingwiki/RLE_Compression

请注意,有时候,数据最终会比原来的 大,原因很简单,因为RLE中没有足够的重复字节可供使用。处理此类压缩失败的一种好方法是在最终数据中添加标头。如果您只是在开始时为未压缩数据添加一个额外字节,对于RLE压缩数据添加1,那么,当RLE未能给出较小的结果时,您只需将其保存为未压缩,前面为0,并且您的最终数据将比原始数据大一个字节。然后,另一方的系统可以读取该起始字节并使用它来确定以下数据是应该解压缩还是仅复制。