【问题标题】:C# Compress a string to a smaller stringC# 将字符串压缩为更小的字符串
【发布时间】:2021-05-05 05:05:25
【问题描述】:

我想将一个字符串(任何大小),如“你好,我的名字是 Bob,我做得很好”,压缩成一个较小的字符串,如“jg3K9dlj”。并且能够解压回来。 Input 和 Output 应该都是两个字符串。

我发现这个代码很容易使用:2 个函数 Compress() 和 Decompress()。 不幸的是,结果它给出了更长的字符串。 我还发现了其他使用 Bytes Array 但无法将它们显示为字符串的示例(完全不可读)。每次我使用 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);
        }

【问题讨论】:

  • 你必须使用直接对字符串进行操作的压缩算法。
  • 请记住:压缩有一定的开销。因此,对于非常小的输入,通常压缩增益很可能超过该开销。你对此无能为力。除了了解压缩仅适用于 更大 数量的数据。 (作为进一步阅读,我推荐The Pigeonhole Principle
  • 您可以尝试使用预训练字典的 zlib,但我不知道 C# 实现。有关的; stackoverflow.com/questions/479218/…
  • @FranzGleichmann 鸽巢原则不适用于 string => byte[] 压缩。由于某些 byte[] 序列不是有效的 unicode 字符串,因此应该可以确保“压缩”字节数组永远不会比输入字符串长。
  • @JeremyLakeman 鸽巢原则适用于 every 压缩。因为最后,您将一组字节压缩为另一组字节 - “字符串”只是它之上的抽象。

标签: c# string compression


【解决方案1】:

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

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

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

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

然后您可以为"Hello My name is Bob, Im doing fine" (35) 获得"#1 my #4 is Bob, Im #2 #3"(25 个字符,节省 28%)。除非给出标点符号或当它没有意义时(在字符串的末尾),否则定义替换后始终跟一个空格之类的技巧可以让您进一步将其减少到 "#1my #4is Bob, Im #2#3"(22 个字符,节省 37%)。

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

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

记住 2:替换映射必须是硬编码的,保存在配置中,或者必须带有压缩数据!

说了这么多,如果您想节省磁盘空间并且知道您的字符串将只包含属于 ASCII 字符集的字符,您可以通过将字符串重新编码为 ASCII(8位字符)来自 C# 的默认值(16 位 Unicode)(但请注意通过为文件指定相同的编码来确保这是将它们写入磁盘的方式)。这可以通过替换压缩来完成,并且两者中可能会提供更大的空间节省。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      相关资源
      最近更新 更多