Safe Filestream

08/23/2011 09:50 BaussHacker#1
This is a class I just made for fun, what can you use it for?

Well, it's just a normal filestream with streamwriter/reader, you can modify it if you wish to.

However, it will always make sure you can read/write to the chosen file and it will not throw and exception, if the file is already open in a stream, because if it is, then it will close it.

Code:
    public class SafeCollector
    {
        public static List<SafeFileStream> Streams = new List<SafeFileStream>();
    }
    public class SafeFileStream
    {
        private System.IO.FileStream fstream;
        private string m_filename;

        public SafeFileStream(string FileName, bool writeall)
        {
            int count = 0;
            m_filename = FileName;
        again:
            if (count <= 1)
            {
                count++;
                try
                {
                    if (!writeall)
                        fstream = new System.IO.FileStream(FileName, System.IO.FileMode.Open);
                    else
                        fstream = new System.IO.FileStream(FileName, System.IO.FileMode.Truncate);
                }
                catch
                {
                    try
                    {
                        fstream = new System.IO.FileStream(FileName, System.IO.FileMode.CreateNew);
                    }
                    catch
                    {
                        foreach (SafeFileStream stream in SafeCollector.Streams)
                        {
                            if (stream.m_filename == FileName)
                            {
                                stream.Close();
                                goto again;
                            }
                        }
                    }
                }
            }

            if (!SafeCollector.Streams.Contains(this))
                SafeCollector.Streams.Add(this);
        }

        public void Write(string content)
        {
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fstream))
            {
                sw.WriteLine(content);
                sw.Close();
            }
        }

        public string Read()
        {
            string ret = "";
            using (System.IO.StreamReader sr = new System.IO.StreamReader(fstream))
            {
                ret = sr.ReadToEnd();
                sr.Close();
            }
            return ret;
        }

        public void Close()
        {
            fstream.Close();

            if (SafeCollector.Streams.Contains(this))
                SafeCollector.Streams.Remove(this);
        }

        ~SafeFileStream()
        {
            if (SafeCollector.Streams.Contains(this))
                SafeCollector.Streams.Remove(this);
        }
    }
This:
Code:
            SafeFileStream fs = new SafeFileStream("test.txt", true);
            fs.Write("OMFG\nTROLOLOL");
            fs.Close();

            SafeFileStream fss = new SafeFileStream("test.txt", false);
            string s = fss.Read();
            fss.Close();
Will work just the same as:
Code:
            SafeFileStream fs = new SafeFileStream("test.txt", true);
            fs.Write("OMFG\nTROLOLOL");

            SafeFileStream fss = new SafeFileStream("test.txt", false);
            string s = fss.Read();
            fss.Close();
08/23/2011 10:06 Spirited#2
Pointers and flash copying to arrays and to strings ftw.
This is ok too though. :awesome:
08/23/2011 12:26 -impulse-#3
It's not really OK. Quit on using try - catch. There are functions you can call to get rid of try catch like ... in your example you should filtrate the 'file in use exception' (first try). But before that you should check if the file exists.

EDIT:
It would be safer to use File.WriteAllBytes, File.ReadAllBytes, File.WriteAllText, File.ReadAllText than using streams. Well, unless you deal with data that is too big and cant be put all together in your ram, you should use the functions I just mentioned. A good example for not using this approach would be writing your own upload/download to a server. If the file is too big you don't have enough memory which means you'll have to read blocks and send them, usually 1024 bytes blocks.

Now, if you want to use for example WriteAllBytes, you'd need something to store the data before you write it which comes down to a stream, obviously unless you want to make a class to handle a buffer that is changing all the time it's no problem. Whenever I deal with files I use a MemoryStream and BinaryWriter over that memory stream, that way I have a class to handle the buffer writing, then all I have to do is simply call File.WriteAllBytes(path, memstream.ToArray()); The function will take care of file, closing it so I dont have to worry about having troubles some other time to write or open it. Also if you want to use WriteAllText, you can keep your data in a 'string' variable but it would cost too much time to modify it all the time as in:

Code:
string text = "";
text += "Name=" + client.Name + Environment.NewLine;
text += "Age=" + client.Age + Environment.NewLine;
...
Whenever I have to deal with strings, I use a StringBuilder, because it will act like a memory stream, as in whenever there is need of more space in buffer it won't just create a new buffer like sizeBefore + adding.Size so the new string would fit in but more than that idk, usually I've seen *2 size than now or w/e so it won't lose time on recreating the buffer on every append you do. (when you do like "this string" + "this other string" + "this another string" it does like:
1. create a new buffer["this string".Length + "this other string".Length], and copy the values one after another in the buffer
2. create another buffer[first buffer.Length + "this another string".Length] and copy the first buffer and the other string in it
Using string builder it will give you a boost whenever handling strings.
Also, this being said here's an example:
Code:
StringBuilder builder = new StringBuilder();
builder.Append("Name=");
builder.AppendLine(client.Name); //will write the argument at the current position and it will add a new line after it
builder.Append("Age=");
builder.AppendLine(client.Age); //You might need to do .ToString() if your variable is not a string
...
string text = builder.ToString();
Have fun!