【问题标题】:Why does one method of string compression return an empty string but another one doesn't?为什么一种字符串压缩方法返回一个空字符串而另一种没有?
【发布时间】:2012-06-26 21:21:24
【问题描述】:

我正在使用GZipStream 来压缩一个字符串,并且我修改了两个不同的示例来看看什么是有效的。第一个代码 sn-p 是 the example in the documentation 的一个经过大量修改的版本,它只返回一个空字符串。

public static String CompressStringGzip(String uncompressed)
{
    String compressedString;
    // Convert the uncompressed source string to a stream stored in memory
    // and create the MemoryStream that will hold the compressed string
    using (MemoryStream inStream = new MemoryStream(Encoding.Unicode.GetBytes(uncompressed)),
                        outStream = new MemoryStream())
    {
        using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress))
        {
            inStream.CopyTo(compress);
            StreamReader reader = new StreamReader(outStream);
            compressedString = reader.ReadToEnd();
        }
    }
    return compressedString;

当我调试它时,我只能说没有从reader 中读取,即compressedString 是空的。但是,我写的第二种方法,从CodeProject snippet 修改是成功的。

public static String CompressStringGzip3(String uncompressed)
{
    //Transform string to byte array
    String compressedString;
    byte[] uncompressedByteArray = Encoding.Unicode.GetBytes(uncompressed);

    using (MemoryStream outStream = new MemoryStream())
    {
        using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress))
        {
            compress.Write(uncompressedByteArray, 0, uncompressedByteArray.Length);
            compress.Close();
        }
        byte[] compressedByteArray = outStream.ToArray();
        StringBuilder compressedStringBuilder = new StringBuilder(compressedByteArray.Length);
        foreach (byte b in compressedByteArray)
            compressedStringBuilder.Append((char)b);
        compressedString = compressedStringBuilder.ToString();
    }
    return compressedString;
}

为什么第一个代码 sn -p 不成功,而另一个是?尽管它们略有不同,但我不知道为什么第二个 sn-p 中的微小变化允许它工作。我使用的示例字符串是SELECT * FROM foods f WHERE f.name = 'chicken';

【问题讨论】:

  • 与流的位置有关吗?在阅读之前,您是否尝试过在方法 1 中寻找流的开头?
  • 我在行前添加了inStream.Seek(0L, SeekOrigin.Begin);inStream.CopyTo(compress);,但该方法仍然返回一个空字符串。

标签: c# string gzipstream


【解决方案1】:

您需要将下面的代码移到第二个 using 语句之外:

using (GZipStream compress = new GZipStream(outStream, CompressionMode.Compress)) 
{ 
    inStream.CopyTo(compress); 
    outStream.Position = 0;
    StreamReader reader = new StreamReader(outStream); 
    compressedString = reader.ReadToEnd(); 
}

CopyTo() 不会将结果刷新到底层 MemoryStream。

更新

似乎 GZipStream 在处理时关闭并处理它的底层流(不是我设计类的方式)。我已经更新了上面的示例并进行了测试。

【讨论】:

  • 在第二个(内部?)using 声明之外?如果我将有问题的代码移到那里,我会收到一条错误消息,指出 outStream 无法读取。
  • 使用 stream.Flush() 手动刷新流怎么样?
  • @Charleh 也可以,但我认为在 using 语句之外执行此操作更具可读性,因为它清楚地将两个任务分开。
  • 是的,主要是针对 outStream 不可读的问题 - 不知道为什么会这样,因为在外部 using 语句结束之前没有任何处理流
  • @Slugart 该代码给了我一个错误:Cannot access a closed Stream
【解决方案2】:

我最终使用以下代码进行压缩和解压缩:

public static String Compress(String decompressed)
{
    byte[] data = Encoding.UTF8.GetBytes(decompressed);
    using (var input = new MemoryStream(data))
    using (var output = new MemoryStream())
    {
        using (var gzip = new GZipStream(output, CompressionMode.Compress, true))
        {
            input.CopyTo(gzip);
        }
        return Convert.ToBase64String(output.ToArray());
    }
}

public static String Decompress(String compressed)
{
    byte[] data = Convert.FromBase64String(compressed);
    using (MemoryStream input = new MemoryStream(data))
    using (GZipStream gzip = new GZipStream(input, CompressionMode.Decompress))
    using (MemoryStream output = new MemoryStream())
    {
        gzip.CopyTo(output);
        StringBuilder sb = new StringBuilder();
        return Encoding.UTF8.GetString(output.ToArray());

    }
}

部分问题的解释来自this question。尽管我通过将代码更改为我在此答案中包含的内容来解决问题,但这些行(在我的原始代码中):

foreach (byte b in compressedByteArray)
            compressedStringBuilder.Append((char)b);

是有问题的,因为正如dlev 恰当地表述它:

您将每个字节解释为自己的字符,而实际上并非如此。相反,您需要以下行:

string decoded = Encoding.Unicode.GetString(compressedByteArray);

基本问题是您正在转换为基于编码的字节数组,但在检索字节时忽略了该编码。

因此,问题解决了,我使用的新代码比我原来的代码简洁多了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多