【问题标题】:Reading compressed file and writing to new file will not allow decompression读取压缩文件并写入新文件将不允许解压缩
【发布时间】:2011-07-13 15:05:08
【问题描述】:

我有一个测试程序来演示我希望的最终结果(即使在这个测试程序中这些步骤似乎没有必要)。

程序使用 GZipStream 将数据压缩到文件中。生成的压缩文件是 C:\mydata.dat

然后我读取这个文件,并将它写入一个新文件。

//Read original file
string compressedFile = String.Empty;
using (StreamReader reader = new StreamReader(@"C:\mydata.dat"))
{
    compressedFile = reader.ReadToEnd();
    reader.Close();
    reader.Dispose();
}

//Write to a new file
using (StreamWriter file = new StreamWriter(@"C:\mynewdata.dat"))
{
    file.WriteLine(compressedUserFile);
}

当我尝试解压这两个文件时,原始文件完美解压,但新文件抛出 InvalidDataException 并显示消息 GZip 标头中的幻数不正确。确保您传递的是 GZip 流。

为什么这些文件不同?

【问题讨论】:

  • 你为什么用string而不是byte[]
  • 看起来像 File.Copy 的工作(不是?)
  • reader.ReadToEnd() 返回一个字符串
  • 另外,我不能使用 File.Copy...我正在读取文件,将其上传到不同的位置,并在那里创建一个新文件...这只是一个简化版本手头的问题
  • @John:您应该一次读取一大块字节。我会发布一个简短的答案

标签: c# file-io gzip filestream gzipstream


【解决方案1】:

StreamReader 用于读取 字符 序列,而不是字节。这同样适用于StremWriter。由于将压缩文件视为字符流没有任何意义,因此您应该使用Stream 的一些实现。如果您想将流作为字节数组获取,可以使用MemoryStream

使用字符流不起作用的确切原因是它们默认采用 UTF-8 编码。如果某些字节不是有效的 UTF-8(如标头的第二个字节 0x8B),则表示为 Unicode“替换字符”(U+FFFD)。当字符串被写回时,该字符会使用 UTF-8 编码为与源中完全不同的内容。

例如,要从流中读取文件,将其作为字节数组获取,然后将其作为流写入另一个文件:

byte[] bytes;
using (var fileStream = new FileStream(@"C:\mydata.dat", FileMode.Open))
using (var memoryStream = new MemoryStream())
{
    fileStream.CopyTo(memoryStream);
    bytes = memoryStream.ToArray();
}

using (var memoryStream = new MemoryStream(bytes))
using (var fileStream = new FileStream(@"C:\mynewdata.dat", FileMode.Create))
{
    memoryStream.CopyTo(fileStream);
}

CopyTo() 方法仅在 .Net 4 中可用,但 you can write your own 如果您使用旧版本。

当然,对于这个简单的例子,没有必要使用流。你可以这样做:

byte[] bytes = File.ReadAllBytes(@"C:\mydata.dat");
File.WriteAllBytes(@"C:\mynewdata.dat", bytes);

【讨论】:

    【解决方案2】:

    编辑:显然,我的建议是错误的/无效的/无论如何...请使用毫无疑问已经高度重构到无法实现额外性能的其他建议之一(否则,那将意味着它们和我的一样无效)

    using (System.IO.StreamReader sr = new System.IO.StreamReader(@"C:\mydata.dat"))
    {
        using (System.IO.StreamWriter sw = new System.IO.StreamWriter(@"C:\mynewdata.dat"))
        {
            byte[] bytes = new byte[1024];
            int count = 0;
            while((count = sr.BaseStream.Read(bytes, 0, bytes.Length)) > 0){
                sw.BaseStream.Write(bytes, 0, count);
            }
        }
    }
    

    读取所有字节

    byte[] bytes = null;
    using (System.IO.StreamReader sr = new System.IO.StreamReader(@"C:\mydata.dat"))
    {
        bytes = new byte[sr.BaseStream.Length];
        int index = 0;
        int count = 0;
        while((count = sr.BaseStream.Read(bytes, index, 1024)) > 0){
            index += count;
        }
    }
    

    读取所有字节/写入所有字节(来自 svick 的回答):

    byte[] bytes = File.ReadAllBytes(@"C:\mydata.dat");
    File.WriteAllBytes(@"C:\mynewdata.dat", bytes);
    

    其他答案的性能测试:

    刚刚在我的答案(StreamReader)(上面的第一部分,文件副本)和 svick 的答案(FileStream/MemoryStream)(第一个)之间进行了快速测试。测试是代码的 1000 次迭代,以下是 4 次测试的结果(结果以整秒为单位,所有实际结果都略高于这些值):

    My Code | svick code
    --------------------
    9       | 12
    9       | 14
    8       | 13
    8       | 14
    

    如您所见,至少在我的测试中,我的代码表现得更好。我可能要注意的一件事是我没有读取字符流,实际上我正在访问提供字节流的 BaseStream。也许 svick 的回答很慢,因为他使用两个流进行读取,然后使用两个流进行写入。当然,svick 的回答可以做很多优化来提高性能(他还提供了简单文件复制的替代方案)

    使用第三个选项进行测试 (ReadAllBytes/WriteAllBytes)

    My Code | svick code | 3rd
    ----------------------------
    8       | 14         | 7
    9       | 18         | 9
    9       | 17         | 8
    9       | 17         | 9
    

    注意:以毫秒为单位,第三个选项总是更好

    【讨论】:

    • 感谢您的回复...但是如果我无法同时访问这两个文件,我需要某种临时存储...我可以将其读入一个字节[]?
    • Read() 不需要返回输入流中的所有字节,即使它们适合数组。例如,对于NetworkStream,这种情况经常发生。但是所有其他Streams 都可以这样做。如果你想以这种方式使用Read(),你必须确保没有更多字节要读取。
    • 为什么在读取字节时还要创建StreamReader?直接使用FileStream即可。
    • 我使用了 OP 使用的东西,老实说,我不记得内存/文件流的来龙去脉了。无论如何我都支持你的答案....但我认为我的反对票是不公平的,因为我提供了一个有效的(尽管是替代的)解决方案
    • svick 已经提到你应该只使用Stream(字节)而不是StreamReader(字符)。性能与讨论绝对无关;关键是您承认发布了一个没有解释的误导性代码示例,没有花时间查看详细信息。
    猜你喜欢
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-03
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多