【问题标题】:C# Overwriting file with StreamWriter created from FileStreamC# 使用从 FileStream 创建的 StreamWriter 覆盖文件
【发布时间】:2016-02-12 07:13:11
【问题描述】:

我需要操作文件的内容:

 FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);               
 StreamReader sr = new StreamReader(fs);
 StreamWriter sw = new StreamWriter(fs);
 newString = someStringTransformation(sr.ReadToEnd());
 sw.Write(newString);
 fs.flush();
 fs.Close();

但是,上面添加了 newString 而不是用新的更改覆盖文件。需要这样做,以便在读取写入之间没有其他应用程序可以访问该文件,这就是我从 FileStream 对象创建读取器和写入器的原因。

我知道您可以创建一个将第二个参数设置为 false 的 StreanWriter,如 here 所述。但是,在创建上述 StreamWriter 时,这似乎不是参数之一。

【问题讨论】:

  • 尝试刷新流并将其重新定位到文件的开头,然后写入,然后刷新写入器,然后截断文件。
  • 另一种方法是创建另一个临时文件,将所有新内容保存到该文件中,然后删除旧文件并重命名新文件。
  • 您尝试做的事情毫无意义,您无法防止另一个进程在打开文件前一微秒读取文件。
  • 旁注:使用IDisposable(例如FileStream)时,将其包装到using(FileStream fs = new FileStream..) {...} 中比调用Close 更好(您可能会在异常情况下发生资源泄漏)

标签: c# file


【解决方案1】:

您遇到的问题是从流中读取到文件末尾。然后将追加进一步的写入。

这将实现完全覆盖。

using(FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
    StreamReader sr = new StreamReader(fs);
    using (StreamWriter sw = new StreamWriter(fs))
    {
        newString = someStringTransformation(sr.ReadToEnd());

        // discard the contents of the file by setting the length to 0
        fs.SetLength(0); 

        // write the new content
        sw.Write(newString);
    }
}

为什么使用SetLength?您的新内容可能比现有字符串短!您最不想要的是文件末尾的旧内容。

【讨论】:

  • 别忘了.Close()StreamWriterStreamReader。或者,它们可以封装在 using() { } 块中。
【解决方案2】:

您需要在这里采取几个步骤,但让我澄清一下我的假设:

您需要在整个操作过程中保持文件打开和锁定,以防止其他人在此期间访问该文件。

话虽如此,这就是你需要做的:

  1. 您需要使用StreamReader 阅读内容,就像您所做的那样
  2. 您需要将底层流重新定位到开头,它的位置已通过阅读器进行更改
  3. 你需要通过StreamWriter写出转换后的内容,和你一样
  4. 由于下一步,您需要刷新写入器
  5. 您需要将基础流/文件截断到其当前位置,以处理缩短内容的转换。

所有这些的代码可能看起来像这样LINQPad 程序:

void Main()
{
    const string filePath = @"d:\temp\test.txt";
    var encoding = Encoding.UTF8;
    using (var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
    using (var reader = new StreamReader(stream, encoding))
    using (var writer = new StreamWriter(stream, encoding))
    {
        // Read
        var contents = reader.ReadToEnd();

        // Transform
        var transformedContents = contents.Substring(0, Math.Max(0, contents.Length - 1));

        // Write out transformed contents from the start of the file
        stream.Position = 0;
        writer.Write(transformedContents);
        writer.Flush();

        // Truncate
        stream.SetLength(stream.Position);
    }
}

【讨论】:

    【解决方案3】:

    您可以避免这些低级的Stream 和他们的Reader/Writers 使用Linq

      File.WriteAllText(filePath, someStringTransformation(File.ReadAllText(filePath)));
    

    【讨论】:

      【解决方案4】:

      您可以做的是重新定位流并删除缓冲数据,以确保没有任何阻碍。以你为例:

      FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);               
       StreamReader sr = new StreamReader(fs);
       StreamWriter sw = new StreamWriter(fs);
       newString = someStringTransformation(sr.ReadToEnd());
      
          sr.Position = 0;
          sr.DiscardBufferedData(); 
      
          sw.Position = 0;
      
       sw.Write(newString);
       fs.flush();
       fs.Close();
      

      如果新数据少于旧数据,则需要截断剩余数据。通过使用sw.SetLength(newString.Length);

      【讨论】:

        【解决方案5】:

        也许会有所帮助。

        只需使用FileMode.OpenFileMode.Truncate 覆盖文件:

        namespace System.IO
        {
            //
            // Summary:
            //     Specifies how the operating system should open a file.
            [ComVisible(true)]
            public enum FileMode
            {
                ...
                //
                // Summary:
                //     Specifies that the operating system should create a new file. If the file already
                //     exists, it will be overwritten. This requires System.Security.Permissions.FileIOPermissionAccess.Write
                //     permission. FileMode.Create is equivalent to requesting that if the file does
                //     not exist, use System.IO.FileMode.CreateNew; otherwise, use System.IO.FileMode.Truncate.
                //     If the file already exists but is a hidden file, an System.UnauthorizedAccessException
                //     exception is thrown.
                Create = 2,
                //
                ...
            }
        

        namespace System.IO
        {
            //
            // Summary:
            //     Specifies how the operating system should open a file.
            [ComVisible(true)]
            public enum FileMode
            {
                ...
                //
                // Summary:
                //     Specifies that the operating system should open an existing file. When the file
                //     is opened, it should be truncated so that its size is zero bytes. This requires
                //     System.Security.Permissions.FileIOPermissionAccess.Write permission. Attempts
                //     to read from a file opened with FileMode.Truncate cause an System.ArgumentException
                //     exception.
                Truncate = 5,
                ...
            }
        

        【讨论】:

          【解决方案6】:

          只需使用:

          FileStream fs = System.IO.File.Create(filePath);
          

          File.Create 将创建或覆盖文件并为其返回文件流。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-01-31
            • 2019-09-18
            • 1970-01-01
            • 2015-05-07
            • 2014-10-05
            • 2012-10-15
            • 2013-05-01
            相关资源
            最近更新 更多