Reputation: 1071
I need to manipulate the contents of a file:
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();
However the above appends the newString instead of overwriting the file with the new changes. It needs to be done so that no other application can access the file in between reading a writing which is why I'm creating the reader and writer from a FileStream object.
I know that you can create a StreanWriter with the second parameter set to false as described here. However when creating the StreamWriter as above that does not seem to be one of the parameters.
Upvotes: 24
Views: 35151
Reputation: 66
Just use:
FileStream fs = System.IO.File.Create(filePath);
File.Create will create or overwrite a file and return the filestream for it.
Upvotes: 5
Reputation: 1162
Maybe it will help one.
Just use FileMode.Open
or FileMode.Truncate
to overwrite the file:
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,
//
...
}
or
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,
...
}
Upvotes: 3
Reputation: 14334
The problem you are having is that reading from the stream advances to the end of the file. Further writes will then append.
This will achieve a full overwrite.
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);
}
}
Why use SetLength
? Your new content might be shorter than the existing string! The last thing you want is the old content at the end of your file.
Upvotes: 38
Reputation: 186668
You can avoid these low-level Stream
's and their Reader
/Writer
s by using Linq:
File.WriteAllText(filePath, someStringTransformation(File.ReadAllText(filePath)));
Upvotes: 2
Reputation: 2984
What you could do is repositioning the streams and also removing buffered data to be sure that nothing gets in the way. Taking your example:
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();
if the new data is less than the old data you would need to truncate the remaining data. By using sw.SetLength(newString.Length);
.
Upvotes: 1
Reputation: 391276
There are several steps you need to take here but let me make my assumptions clear:
You need to keep the file open and locked during the entire operation to prevent others from accessing the file during this time.
With that said, here's what you need to do:
StreamReader
, as you've doneStreamWriter
, as you've doneThe code for all of this could look like this LINQPad program:
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);
}
}
Upvotes: 4