【问题标题】:Close a filestream without Flush()关闭没有 Flush() 的文件流
【发布时间】:2011-11-14 16:08:03
【问题描述】:

我可以关闭文件流而不调用Flush(在 C# 中)吗?我理解CloseDispose首先调用Flush方法。

【问题讨论】:

  • 您为什么要这样做?如果你不刷新,你不知道文件中有什么,什么不是 - 所以你会在未定义的状态下关闭它。
  • 你的意思是你不希望流在你关闭时也刷新?
  • 什么意思?您不想遗漏手动刷新(这可能是因为 Close/Dispose 调用 Flush)还是要避免自动刷新(不可能,也是一个坏主意)?
  • Close 会刷新 FileStream 的缓冲区,但不会刷新与任何具有单独缓冲区的 StreamReader/Writer 关联的缓冲区。只要您正在阅读就没有问题,但是如果您使用 StreamWriter 或任何其他具有自己缓冲区的包装器,您就会遇到麻烦。
  • Environment.FailFast() 有效。使用反射从 FileStream 中挖掘私有 _handle 字段并调用 CloseHandle() 也可以,您必须从 Close() 中捕获异常

标签: c# filestream flush


【解决方案1】:

MSDN 不是 100% 清楚,但 Jon Skeet 说“冲洗”,所以在关闭/处置之前这样做。不会痛吧?

来自FileStream.Close Method

之前写入缓冲区的任何数据都会在之前复制到文件中 文件流是关闭的,所以之前不需要调用 Flush 调用关闭。在调用 Close 之后,对文件的任何操作 流可能会引发异常。在 Close 被调用一次后,它 如果再次调用,则什么也不做。

处置不是很清楚:

此方法通过将任何更改写入支持来处理流 存储和关闭流以释放资源。

备注:评论员可能是对的,不是 100% 从同花顺中清楚:

在实现缓冲区的流上覆盖 Flush。使用此方法 将任何信息从底层缓冲区移动到其目的地, 清除缓冲区,或两者兼而有之。根据对象的状态,您 可能必须修改流中的当前位置(对于 例如,如果底层流支持查找)。对于额外的 信息见 CanSeek。

当使用 StreamWriter 或 BinaryWriter 类时,不要刷新 基本流对象。相反,使用类的 Flush 或 Close 方法, 确保将数据刷新到底层流 先写入文件。

测试:

var textBytes = Encoding.ASCII.GetBytes("Test123");
using (var fileTest = System.IO.File.Open(@"c:\temp\fileNoCloseNoFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes,0,textBytes.Length);
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseNoFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Close();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileFlushNoClose.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseAndFlush.txt", FileMode.CreateNew))
{
    fileTest.Write(textBytes, 0, textBytes.Length);
    fileTest.Flush();
    fileTest.Close();
}

我能说什么...所有文件都有文本 - 也许这只是数据太少?

测试2

var rnd = new Random();
var size = 1024*1024*10;
var randomBytes = new byte[size];
rnd.NextBytes(randomBytes);
using (var fileTest = System.IO.File.Open(@"c:\temp\fileNoCloseNoFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseNoFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Close();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileFlushNoClose.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Flush();
}
using (var fileTest = System.IO.File.Open(@"c:\temp\fileCloseAndFlush.bin", FileMode.CreateNew))
{
    fileTest.Write(randomBytes, 0, randomBytes.Length);
    fileTest.Flush();
    fileTest.Close();
}

再一次 - 每个文件都有它的字节......对我来说,它看起来就像我从 MSDN 中读到的一样:在 dispose 之前调用 Flush 或 Close 都没关系......对此有什么想法吗?

【讨论】:

  • "复制到文件" - 这不正是冲洗的作用吗?我读到这个 ​​Flush() 没有被调用,但缓冲区无论如何都会被刷新。
  • 按照我的阅读方式,您无法在不冲洗的情况下关闭它。即使您没有明确调用它们,它们也会为您刷新。
  • 是的——你可以打电话给Close()而不打电话给Flush(),但是你不能在不刷新的情况下关闭:)
  • 好的 - 我想伟大的乔恩会知道的......我会改变我的帖子
  • 阅读障碍牙医的建议:关闭前冲洗。
【解决方案2】:

不必Close()/Dispose()上调用Flush()FileStream会为你做这件事,你可以从它的源代码中看到:

http://referencesource.microsoft.com/#mscorlib/system/io/filestream.cs,e23a38af5d11ddd3

    [System.Security.SecuritySafeCritical]  // auto-generated
    protected override void Dispose(bool disposing)
    {
        // Nothing will be done differently based on whether we are 
        // disposing vs. finalizing.  This is taking advantage of the
        // weak ordering between normal finalizable objects & critical
        // finalizable objects, which I included in the SafeHandle 
        // design for FileStream, which would often "just work" when 
        // finalized.
        try {
            if (_handle != null && !_handle.IsClosed) {
                // Flush data to disk iff we were writing.  After 
                // thinking about this, we also don't need to flush
                // our read position, regardless of whether the handle
                // was exposed to the user.  They probably would NOT 
                // want us to do this.
                if (_writePos > 0) {
                    FlushWrite(!disposing); // <- Note this
                }
            }
        }
        finally {
            if (_handle != null && !_handle.IsClosed)
                _handle.Dispose();

            _canRead = false;
            _canWrite = false;
            _canSeek = false;
            // Don't set the buffer to null, to avoid a NullReferenceException
            // when users have a race condition in their code (ie, they call
            // Close when calling another method on Stream like Read).
            //_buffer = null;
            base.Dispose(disposing);
        }
    }

【讨论】:

    【解决方案3】:

    既然您已经声明您理解 close & dispose 称为 flush 方法,如果它没有被用户代码显式调用,我相信(通过 close without flush)您实际上想要拥有如有必要,可以放弃FileStream 所做的更改。

    如果这是正确的,单独使用 FileStream 将无济于事。您需要将此文件加载到 MemoryStream(或数组,具体取决于您修改其内容的方式)中,然后在完成后决定是否要保存更改。

    这显然是文件大小的问题。 FileStream 使用有限大小的写入缓冲区来加速操作,但是一旦它们耗尽,就需要刷新更改。由于 .NET 内存限制,您只能在内存中加载较小的文件,如果您需要完全保存它们。

    更简单的替代方法是制作文件的副本磁盘,然后使用普通的FileStream 处理它。完成后,如果您需要放弃更改,只需删除临时文件,否则将原始文件替换为修改后的副本。

    【讨论】:

      【解决方案4】:

      我一直在跟踪一个新引入的错误,该错误似乎表明 .NET 4 在处理流时无法可靠地将更改刷新到磁盘(与 .NET 2.0 和 3.5 不同,它们总是如此可靠)。

      .NET 4 FileStream 类在 .NET 4 中进行了大量修改,虽然 Flush*() 方法已被重写,但 .Dispose() 似乎已经忘记了类似的注意事项。

      这会导致文件不完整。

      【讨论】:

      • 找到修复了吗?
      【解决方案5】:

      FileStream 包装在BufferedStream 中,并在缓冲流之前关闭文件流。

      var fs = new FileStream(...);
      var bs = new BufferedStream(fs, buffersize);
      
      bs.Write(datatosend, 0, length);
      
      fs.Close();
      try {
          bs.Close();
      }
      catch (IOException) {
      }
      

      【讨论】:

        【解决方案6】:

        在大循环中使用 Flush() 是值得的。 当您必须在一个循环中读取和写入一个大文件时。在其他情况下,缓冲区或计算机足够大,并且在不进行 Flush() 之前关闭() 也没关系。

        示例:您必须阅读一个大文件(以一种格式)并将其写入 .txt

         StreamWriter sw =  ....    // using StreamWriter
        // you read the File  ...
        // and now you want to write each line for this big File using WriteLine ();
        
        
        for ( .....)    // this is a big Loop because the File is big and has many Lines
        
        {
        
         sw.WriteLine ( *whatever i read* );  //we write here somrewhere ex. one .txt anywhere
        
         sw.Flush();  // each time the sw.flush() is called, the sw.WriteLine is executed
        
        }
        
        sw.Close();
        

        这里使用 Flush() 非常重要;因为否则每个 writeLine 都保存在缓冲区中,直到缓冲区满或程序到达 sw.close();

        希望对理解Flush的功能有点帮助

        【讨论】:

        • 欢迎使用 StackOverflow 并感谢您的回答,但它并没有真正回答“我是否需要在关闭前手动刷新”的问题
        【解决方案7】:

        我认为使用简单的 using 语句是安全的,它在调用 GetBytes() 后关闭流;

        public static byte[] GetBytes(string fileName)
        {
            byte[] buffer = new byte[4096];
            using (FileStream fs = new FileStream(fileName)) 
            using (MemoryStream ms = new MemoryStream())
            {
                fs.BlockCopy(ms, buffer, 4096); // extension method for the Stream class
                fs.Close();
                return ms.ToByteArray();
            }
        }
        

        【讨论】:

        • 你为什么在中间打电话给Close
        • @JonSkeet:我认为是因为他犯了错误。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-13
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 2019-10-01
        • 1970-01-01
        • 2012-11-16
        相关资源
        最近更新 更多