3

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

该程序使用 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 流。

为什么这些文件不同?

4

2 回答 2

3

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 中可用,但如果您使用旧版本,您可以自己编写。

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

byte[] bytes = File.ReadAllBytes(@"C:\mydata.dat");
File.WriteAllBytes(@"C:\mynewdata.dat", bytes);
于 2011-07-13T15:45:45.143 回答
-1

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

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

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

于 2011-07-13T15:15:34.910 回答