C# 使用从 FileStream 创建的 StreamWriter 覆盖文件
Posted
技术标签:
【中文标题】C# 使用从 FileStream 创建的 StreamWriter 覆盖文件【英文标题】:C# Overwriting file with StreamWriter created from FileStream 【发布时间】: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
更好(您可能会在异常情况下发生资源泄漏)
【参考方案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()
StreamWriter
和StreamReader
。或者,它们可以封装在 using()
块中。【参考方案2】:
您需要在这里采取几个步骤,但让我澄清一下我的假设:
您需要在整个操作过程中保持文件打开和锁定,以防止其他人在此期间访问该文件。
话虽如此,这就是你需要做的:
-
您需要使用
StreamReader
阅读内容,就像您所做的那样
您需要将底层流重新定位到开头,它的位置已通过阅读器进行更改
你需要通过StreamWriter
写出转换后的内容,和你一样
由于下一步,您需要刷新写入器
您需要将基础流/文件截断到其当前位置,以处理缩短内容的转换。
所有这些的代码可能看起来像这样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
/Writer
s 使用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.Open
或FileMode.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 将创建或覆盖文件并为其返回文件流。
【讨论】:
以上是关于C# 使用从 FileStream 创建的 StreamWriter 覆盖文件的主要内容,如果未能解决你的问题,请参考以下文章
在 C# 中为 FileStream 构造函数引发异常,其中 CreateNew 用于现有文件