Read and Write to File at the same time
Asked Answered
I

6

9

for an application that uses a File as some sort of global storage for device reservations in a firm I need a way to read and write to a file (or lock a file, read from it, write to it, and unlock it). A little code snippet will shot what I mean:

FileStream in = new FileStream("storage.bin", FileMode.Open);
//read the file
in.Close();

//!!!!!
//here is the critical section since between reading and writing, there shouldnt
//be a way for another process to access and lock the file, but there is the chance
//because the in stream is closed
//!!!!!
FileStream out = new FileStream("storage.bin", FileMode.Create);
//write data to file
out.Close();

this should get something like this

LockFile("storage.bin");
//read from it...
//OVERwrite it....
UnlockFile("storage.bin");

the method should be absolute safe, since the program should run on 2000 devices at the same time

Innominate answered 24/7, 2012 at 8:46 Comment(2)
to overcome the issue with the critical section, so ensure that the file doesnt get locked after closing the instreamWarrin
Ultimately it will turn by turn, instead reading and writing at once.Deettadeeyn
Y
15

Simply holding a FileStream open with exclusive (not shared) access will prevent other processes from accessing the file. This is the default when opening a file for read/write access.

You can 'overwrite' a file that you currently hold open by truncating it.

So:

using (var file = File.Open("storage.bin", FileMode.Open))
{
    // read from the file

    file.SetLength(0); // truncate the file

    // write to the file
}

the method should be absolute safe, since the program should run on 2000 devices at the same time

Depending on how often you're writing to the file, this could become a chokepoint. You probably want to test this to see how scalable it is.

In addition, if one of the processes tries to operate on the file at the same time as another one, an IOException will be thrown. There isn't really a way to 'wait' on a file, so you probably want to coordinate file access in a more orderly fashion.

Yield answered 24/7, 2012 at 8:52 Comment(1)
k this helped me, and im waiting on a file in a thread with a loop and a try catch inside, it just opens it until it gets available, which works for me, and didnt know I can write and read in a single filestream, this was basically what I was looking for, will accept in 2 minWarrin
U
3

You need a single stream, opened for both reading and writing.

FileStream fileStream = new FileStream(
      @"c:\words.txt", FileMode.OpenOrCreate, 
      FileAccess.ReadWrite, FileShare.None);

Alternatively you can also try

static void Main(string[] args)
    {
        var text = File.ReadAllText(@"C:\words.txt");
        File.WriteAllText(@"C:\words.txt", text + "DERP");
    }

As per http://msdn.microsoft.com/en-us/library/system.io.fileshare(v=vs.71).aspx

FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.None);

You need to pass in a FileShare enumeration value of None to open on the FileStream constructor overloads:

fs = new FileStream(@"C:\Users\Juan Luis\Desktop\corte.txt", FileMode.Open, 
    FileAccess.ReadWrite, FileShare.None);
Unwritten answered 24/7, 2012 at 8:59 Comment(2)
The combination of ReadAllText/WriteAllText will have the same problem (another process can access the file in between). A single FileStream works, though.Yield
Have edited my comment, kindly have a look at the last 2 approaches, hope it will help. It will produce a "The specified file is in use" error message when someone try to open it with another application.Unwritten
L
2

I ended up writing this helper class to do this:

public static class FileHelper
{
    public static void ReplaceFileContents(string fileName, Func<String, string> replacementFunction)
    {
        using (FileStream fileStream = new FileStream(
                fileName, FileMode.OpenOrCreate,
                FileAccess.ReadWrite, FileShare.None))
        {
            StreamReader streamReader = new StreamReader(fileStream);
            string currentContents = streamReader.ReadToEnd();
            var newContents = replacementFunction(currentContents);
            fileStream.SetLength(0);
            StreamWriter writer = new StreamWriter(fileStream);
            writer.Write(newContents);
            writer.Close();
        }
    }
}

which allows you to pass a function that will take the existing contents and generate new contents and ensure the file is not read or modified by anything else whilst this change is happening

Letourneau answered 24/3, 2016 at 17:21 Comment(1)
Am I wrong or 1. Is there a problem that streamReader is not disposed cleanly, and 2. write is not disposed in case of an error? Are 3 using statements needed here?Mutual
T
1

You are likely looking for FileStream.Lock and FileStream.Unlock

Taliped answered 24/7, 2012 at 8:52 Comment(0)
P
1

I think you just need to use the FileShare.None flag in the overloaded Open method.

file = File.Open("storage.bin", FileMode.Open, FileShare.None);
Pedigree answered 24/7, 2012 at 9:3 Comment(0)
C
0
//lock the file, read all data and write some data
using (FileStream fileStream = new FileStream(
    _configuration.MemoryFileLocation, FileMode.OpenOrCreate,
    FileAccess.ReadWrite, FileShare.None))
{

    //read all lines to a string builder
    string line;
    StreamReader streamReader =
        new StreamReader(fileStream); //do not use the using block here
    StringBuilder readData = new StringBuilder();
    while ((line = streamReader.ReadLine()) != null)
    {
        readData.AppendLine(line);
    }

    //write to the file
    StreamWriter stremWriter = 
        new StreamWriter(fileStream); //do not use the using block here
    stremWriter.WriteLine("Sample data");
    stremWriter.Flush(); //use flush command to write the data to the file

}
Caecilian answered 3/6 at 17:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.