views:

26

answers:

1

Hi, I'd like to open a text file and not allow any other processes to write to it. I understand I can do this with the following FileStream:

Dim fs As New FileStream(_FilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read)

Once I have access like this I need to read all of the lines (I will use a StreamReader(fs)) and then I will need to write to the file (I will use a StreamWriter(fs)).

The problem here is that the StreamWriter won't allow me to set the append mode to false (so I can overwrite all text in the file) when I use the filestream as a parameter instead of the file path. So all text written with the StreamWriter is appended onto the text which I don't want. If I use a StreamWriter with the file path (which will let me set append to false) as the parameter instead of the filestream it will be locked out because of the FileStream FileShare. How can I still have exclusive access to read and write to the file but still be able to overwrite the existing text (append mode false)?

+1  A: 

It is not impossible, you don't actually need a StreamWriter initialized with Apppend = true. All you need to do is use FileStream.Seek() to ensure that the 'file pointer' is positioned at the end of the file. This sample C# code worked well:

        using (var fs = new FileStream(@"c:\temp\test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read)) {
            var sr = new StreamReader(fs);   // NOTE: not using using
            string txt = sr.ReadToEnd();
            fs.Seek(0, SeekOrigin.End);      // Be sure
            var sw = new StreamWriter(fs);
            sw.WriteLine("appended");
            sw.Flush();
        }
    }

Do note the fair amount of discomfort in this code. I would start with FileShare.Read, your preferred value. It implies that you'll let another process read the file while you are tinkering with it. There are good odds that this process is going to get a bit confused about bytes showing up in this file without notice. Could work out well, FileShare.None ensures that your code can never cause any kind of mishap. Strongly recommended.

Next flag is // Note not using using. That's important because StreamReader will take 'responsibility' of the stream you pass it. If you do the standard Right Thing in .NET code, the class is going to close the stream when the Using statement ends its scope. That's not what you want, you want to keep a hold of the file so you can write to it.

The Seek() call does what the StreamWriter constructor's Append argument does, make really sure that you are appending to the file instead of randomly overwrite parts of the file from the beginning. Since you want to append, no problem about making sure that old data in the file is truncated.

The Flush call is the smelly bit here. You have to call it to force the writer to flush its output buffer. If you don't, random bits of what you write will be missing from the file. It is normally not a problem because you'd call Close() or use the Using statement to ensure the writer is closed. Not in this case, the FileStream is calling the shots, it will properly close the file here but it doesn't know that another writer jumped on the bandwagon and wants to write.

This will work but it breaks a fair number of dogmas. The more typical pattern is open-read + read + close, open-createnew + write + close. And dealing with the possibility that the open-createnew might in fail because another process grabbed the file. The hazards of a multi-tasking operating system, always be prepared to deal with that.

This post got kinda long, I probably lost the tl;dr audience. That happens.

Hans Passant
Sorry I must not have been clear enough, I actually need it to be append = false, so I overwrite the entire file with only the lines I need.
Matt
fs.Seek() to the beginning of the file and call fs.SetLength(0) to truncate the file.
Hans Passant