C# 将字符串压缩成更小的字符串

C# Compress a string to a smaller string

我想将一个字符串(任意大小)压缩为一个较小的字符串,如“jg3K9dlj”。并能够解压回来。 输入和输出应该都是两个字符串。

我找到了这段易于使用的代码:2 个函数 Compress() 和 Decompress()。 不幸的是,结果它给出了更长的字符串。 我还发现了其他示例,其中使用了字节数组,但无法将它们显示为字符串(完全不可读)。每次我使用 Convert.ToBase64String(bytes) 时也是如此,然后我们得到比原始字符串更长的字符串。感谢任何建议!

  public static string Compress(string uncompressedString)
        {
            byte[] compressedBytes;

            using (var uncompressedStream = new MemoryStream(Encoding.UTF8.GetBytes(uncompressedString)))
            {
                using (var compressedStream = new MemoryStream())
                {
                    // setting the leaveOpen parameter to true to ensure that compressedStream will not be closed when compressorStream is disposed
                    // this allows compressorStream to close and flush its buffers to compressedStream and guarantees that compressedStream.ToArray() can be called afterward
                    // although MSDN documentation states that ToArray() can be called on a closed MemoryStream, I don't want to rely on that very odd behavior should it ever change
                    using (var compressorStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true))
                    {
                        uncompressedStream.CopyTo(compressorStream);
                    }

                    // call compressedStream.ToArray() after the enclosing DeflateStream has closed and flushed its buffer to compressedStream
                    compressedBytes = compressedStream.ToArray();
                }
            }

            return Convert.ToBase64String(compressedBytes);
        }

     
        public static string Decompress(string compressedString)
        {
            byte[] decompressedBytes;

            var compressedStream = new MemoryStream(Convert.FromBase64String(compressedString));

            using (var decompressorStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
            {
                using (var decompressedStream = new MemoryStream())
                {
                    decompressorStream.CopyTo(decompressedStream);

                    decompressedBytes = decompressedStream.ToArray();
                }
            }

            return Encoding.UTF8.GetString(decompressedBytes);
        }

基本理论:为了在不丢失信息的情况下进行压缩,您必须找到一种方法来用其他需要较少的块来表示数据块space。 “数据块”是指任何有用的、适当的单位,例如字节、双字节、英文单词、位序列等。您在输入和输出上指定了字符串,这意味着我们必须使用字符或字符组。

这意味着几件事:(1) 如果所有字符都可以随机出现且概率相等,则字符串不可压缩 - 永远 (2) 如果您决定对字符串进行采样(以建立有用的分组和频率计数) 然后你必须用压缩数据携带结果(开销) - 总是(3)如果你只处理 - 例如 - 英语文本字符串那么你可以想出一个替换方案但是(4)通常有用, meaningful/worthwhile 可能无法压缩。

一个原始的替换方案可能是这样的(使用低频字符来表示替换,我在这个例子中选择了 #):

## for # (*expands* to 2 characters in compressed string) 
#1 for high-frequency word#1 e.g. "Hello"  
#2 "doing", #3 "fine", #4 "name" 

等等

然后您可以获得 "#1 my #4 is Bob, Im #2 #3"(25 个字符,节省 28%)用于 "Hello My name is Bob, Im doing fine" (35)。像定义替换总是跟在 space 之类的技巧,除非给出标点符号或者当它无意义时(在字符串的末尾)允许您进一步将其减少到 "#1my #4is Bob, Im #2#3" (22 个字符,37节省百分比)。

你没有看到这种事情在野外做太多是有原因的(以及为什么它可能根本不值得你花时间去做,除非你有一个非常具体的用例和一组简单的约束)。考虑一下如何使用我上面的方案压缩“做 3 件事”- "#23 things" 对吗?但是,如果您的替换字典有超过 22 个条目,您是否编码了 word#23word#2 后跟 '3'?为了适应这种情况,您必须放弃一些东西(增加复杂性并可能失去一点可压缩性)。所以我相信你会看到,做一个防弹的、通用的、有价值的方案只有在严格、有限的情况下才是可行的,即使那样也会仔细考虑。

记住 1:递减法则returns:相对于实现该节省所需的额外成本(复杂性),您可以节省多少。

切记2:替换映射必须硬编码,保存在配置中,或者必须用压缩数据携带!

综上所述,如果您想将 space 保存在磁盘上并且知道您的字符串将仅包含属于 ASCII 字符集的字符,则可以将 space 减半通过将字符串从 C# 的默认值(16 位 Unicode)重新编码为 ASCII(8 位字符)的要求(但要注意通过为文件指定相同的编码来确保这是将它们写入磁盘的方式)。这可以通过替换压缩来完成,并且两者可能提供更大的 space-节省。

如果您想到“tinyurl”之类的东西,这些并不是真正的“压缩”,而是数据库查找。

即“你好,我叫 Bob,我做得很好”使用“散列函数”进行转换以生成散列,类似于“jg3K9dlj”,并添加到数据库中。有人可以使用散列在数据库中查找字符串。但是没有数据库,就没有办法从散列到字符串。