File and Stream IO

File and Stream I/O

Computers would essentially be useless without I/O (input and output). Users must have ways of getting information to the computer (keyboard, disk, network, and so on). After the data is in the computer and has been processed, users need a way to get it out (monitor, disk, network, email, and so on). In previous chapters in this book, you saw some examples that used the System.Console.WriteLine and System.Console.ReadlLine commands. Those are basic implementations of input and output streams. In the following sections, you will learn to use and understand file I/O and streams.

Understanding File and Stream I/O

A file is a collection of data, generally stored on some sort of storage media or disk. File I/O is just that: the input and output of data from files.

As defined at, a stream is "a steady flow or succession." In C#, the definition of a stream is similar. It is a flow or succession of data or bytes from a pool of data or storage device. A stream provides for the reading and or writing of data from the current position of the stream. A stream also enables you to jump from one point in the stream to another.

The File Class

The File object is a class that provides high-level functions to make copying, moving, deleting, and opening files easier. In addition, it provides some methods to aid in the creation of FileStreams. To make single calls easier, all the methods provided in the File class are static. Listing 7.1 demonstrates some of the features available in the File class and shows how to create a file using the File class.

Listing 7.1. File Example
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class FileExample   {     [STAThread]     static void Main(string[] args)     {       // Create a new file       string filePath = @"test.txt";       //  Delete the file if it already exists        if(File.Exists(filePath))         File.Delete(filePath);       // Create the file       File.Create(filePath);       System.Console.WriteLine("The file was created.");       System.Console.ReadLine();     }   } } 

The FileInfo Class

The FileInfo object is another class that provides high-level functions to make copying, moving, deleting, and opening files easier. In addition, it provides some methods to aid in the creation of streams. Unlike the File class, the FileInfo class is made up entirely of instance methods. Listing 7.2 demonstrates how to use the FileInfo class to list information about a particular file.


Security checks are made on each call of the static methods in the File class. Because the FileInfo class is an instance class, the security checks need to be performed only once. A general rule of thumb to use when deciding which class to use is this: If you are performing a single operation on a file, the File class might be the right choice. If, however, you are performing multiple operations on a file, the FileInfo class is probably the most efficient choice.

Listing 7.2. FileInfo Example
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {         class Class1         {     [STAThread]     static void Main(string[] args)     {       // Create a new file       string filePath = @"c:\test.txt";       FileInfo fi = new FileInfo(filePath);       //  Print out the information if the file exists       if(fi.Exists)       {         System.Console.WriteLine(           "The file: {0} was last accessed on {1} " +           " and contains {2} bytes of data.", filePath,           fi.LastAccessTime, fi.Length);       }       else         System.Console.WriteLine("The file: {0} does not exist.", filePath);       System.Console.ReadLine();     }   } } 

The Directory Class

Whereas the File and FileInfo classes work on individual files, the Directory class provides methods to copy, move, rename, create, and delete directories . Like the File class, the Directory class provides all static methods. Listing 7.3 demonstrates how to create, rename, and move a directory.

Listing 7.3. Using the Directory Class
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {         class DirectoryExample         {     [STAThread]     static void Main(string[] args)     {       string initialDirectory   = @"c:\InitialDirectory";       string moveDirectoryHere  = @"c:\DirectoryMovedHere";        if(Directory.Exists(initialDirectory))       {         // Delete directory if it exists         Directory.Delete(initialDirectory, true);       }       // Create Directory       Directory.CreateDirectory(initialDirectory);       if(Directory.Exists(moveDirectoryHere))       {         // Delete directory if it exists         Directory.Delete(moveDirectoryHere, true);       }       // Move Directory       Directory.Move(initialDirectory, moveDirectoryHere);       // Create 10 Files in the directory       for(int i=0; i<10; i++)       {         string filePath = moveDirectoryHere + @"\Test" + i.ToString();         File.Create(filePath);       }       // Get all of the files in this directory and list them.       string[] fileList = Directory.GetFiles(moveDirectoryHere, "*.*");       for(int i=0; i<fileList.Length; i++)       {          System.Console.WriteLine(            "File: {0} exists in Directory: {1}",            fileList[i], moveDirectoryHere);       }       System.Console.ReadLine();     }   } } 

Using Streams: FileStream, MemoryStream, StringReader, and StringWriter

All stream classes perform essentially the same functions. They enable you to create, read, write, and close streams of data from some storage medium. The storage medium can be a disk, memory, pipe, or anything else that allows for the temporary of persistent storage of data. In the following sections, you will learn the purpose and how to use a few of these stream classes.

The FileStream Class

The FileStream class is a class that provides methods for opening, closing, reading, and writing to files, pipes, and standard input and output streams. In addition, the FileStream class provides methods to read and write data synchronously and asynchronously. The main benefit to using the FileStream class is that it provides improved performance by buffering input and output. Buffered I/O provides the ability to deal with extremely large files without slowing down or halting the system.

Listing 7.4 demonstrates how to use the FileStream class to print out the contents of a file . This program is similar to the Type command used in DOS. To use this program, type the name of the program and pass it a command-line argument of the file that you want to list.

 Example:  FileStreamExample autoexec.bat 

To enter a command-line argument for a program in Visual Studio.NET, simply right-click on the project (in the Solution Explorer) and click the Properties menu item. Next add the parameter to the Command-Line Arguments item in the Configuration Properties, Debugging section, as shown in Figure 7.1.

Listing 7.4. Using the FileStream Class
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class FileStreamExample   {     static public void OutputFile(string filePath)     {       FileInfo fileInfo = new FileInfo(filePath);       if(fileInfo.Exists)       {         using(FileStream fileStream = fileInfo.OpenRead())         {           byte[] byteArray = new byte[fileInfo.Length];           System.Text.UTF8Encoding temp = new System.Text.UTF8Encoding(true);           while(fileStream.Read(byteArray, 0, byteArray.Length) > 0)           {             Console.WriteLine(temp.GetString(byteArray));           }         }       }     }     [STAThread]     static void Main(string[] args)     {       if(args.Length<1)         System.Console.WriteLine("usage FileStreamExample {filename}");       else         OutputFile(args[0]);     }   } } 

Figure 7.1. Add the command-line argument here when testing the application.

The MemoryStream Class

The MemoryStream class enables you to read and write data not from disk, but from memory. A memory stream is simply an array of unsigned bytes. Because they are stored in memory, memory streams are generally faster than streams that are stored on disk.


If you initialize the memory stream in the constructor with any data, you will not be able to increase the size past the initial length of the stream. Any attempt to do so will result in an Exception being thrown. Therefore, if you aren't 100% sure of the size of the stream at construction time, you'd be better served leaving the size dynamically allocated.

Listing 7.5 demonstrates the simple usage of a memory stream.

Listing 7.5. MemoryStreamExample
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class MemoryStreamExample   {     static protected void AddToStream(Stream stream, string stringMessage)     {       char[] message = stringMessage.ToCharArray();       for(int i=0; i<message.Length; i++)         stream.WriteByte((byte) message[i]);     }     [STAThread]     static void Main(string[] args)     {       string message = "This is a message from the Visual C#.NET Book.";       using(MemoryStream ms = new MemoryStream())       {         AddToStream(ms, message);         ms.Position = 0;         for(; ms.Position<ms.Length; )           System.Console.Write((char) ms.ReadByte());       }       System.Console.ReadLine();     }   } } 

The StringReader and StringWriter Classes

The StringReader class enables you to read from a stream that has a storage medium of a string. The StringWriter classes make it possible for you to write to a stream that uses a StringBuilder as its storage medium. Listing 7.6 demonstrates how to use a StringReader and StringWriter.

Listing 7.6. StringReaderWriterExample
 using System; using System.IO; using System.Text; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class StringReaderWriterExample   {     [STAThread]     static void Main(string[] args)     {       string message    = "This is a message from the Visual C#.NET Book.";       StringBuilder sb  = new StringBuilder(message);       using(StringWriter sw = new StringWriter(sb))       {         sw.Write(" This text was added dynamically.");       }       using(StringReader sr = new StringReader(sb.ToString()))       {         int ch;         while((ch = sr.Read()) != -1)           System.Console.Write((char) ch);       }       System.Console.ReadLine();     }   } } 

Using StreamReader and StreamWriter

The StreamReader and StreamWriter classes are provided to read characters from stream using a specific encoding. Unlike the Stream class, which is used for byte input and output, the StreamReader and StreamWriter classes are used for character input and output. By default, these classes use UTF-8 encoding and are not thread safe. Listing 7.7 demonstrates how to read and write to a file using the StreamReader and StreamWriter classes.

Listing 7.7. StreamReaderWriterExample
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class StreamReaderWriterExample   {     static public void CreateFile(string filePath)      {       using(StreamWriter sw = new StreamWriter(filePath))       {         sw.WriteLine("This is Line One.");         sw.WriteLine("This is Line Two.");         sw.WriteLine("This is Line Three.");         sw.WriteLine("This is Line Four.");         sw.WriteLine("Just to be different Line Five.");       }       System.Console.WriteLine("File created with data using the StreamWriter class.");     }     static public void OutputFile(string filePath)     {       using(StreamReader sr = new StreamReader(filePath))       {         while(sr.Peek() > 0)         {           string lineOfText = sr.ReadLine();           Console.WriteLine(lineOfText);         }       }     }     [STAThread]     static void Main(string[] args)     {       if(args.Length<1)         System.Console.WriteLine("usage FileStreamExample {filename}");       else       {         if(File.Exists(args[0]))           OutputFile(args[0]);         else           CreateFile(args[0]);         System.Console.ReadLine();       }     }   } } 

Using the FileSystemWatcher

Until now, you have learned about and how to use the File, Directory, and Stream classes that are provided in .NET. This enabled you to create, delete, read, and write to and from these objects. This is a very useful thing to know, but what if you need to perform some actions based on these events and you are not the one performing these actions? Fortunately, .NET provides the FileSystemWatcher class. By setting the FileSystemWatcher.NotifyFilter property to one or more of the notification filters, this class enables you to receive a notification when a change occurs to a file or directory. Table 7.1, which is found in the .NET Framework 1.1 MSDN Library, describes the different filters available to you through the FileSystemWatcher class.

Table 7.1. Notification Filters

Member Name



The attributes of the file or folder


The time the file or folder was created


The name of the directory


The name of the file


The date the file or folder was last opened


The date the file or folder last had anything written to it


The security settings of the file or folder


The size of the file or folder

When a file or directory is created, renamed, deleted, or updated, the FileSystemWatcher class generates an event to notify you of the change. However, it is important to realize that more then one notification can be generated for one event. For example, when you create a file and write to that file, you could receive the CreationTime, FileName, LastAccess, and LastWrite notifications. Listing 7.8 demonstrates how to use the FileSystemWatcher class to monitor a directory for added files and then move them to another directory. This enables you to use a directory as sort of an inbox. When a file is received, you can perform some sort of processing and then move it to an archive directory when complete.

Listing 7.8. FileSystemWatcher Example
 using System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 {   class FileSystemWatcherExample   {     protected string                    m_importDir   = string.Empty;     protected string                    m_archiveDir  = string.Empty;     protected FileSystemWatcher         m_watcher     = null;     protected System.Threading.Thread   m_watchThread = null;     public FileSystemWatcherExample(string importDir, string archiveDir)     {       m_importDir  = importDir;       m_archiveDir = archiveDir;       if(!Directory.Exists(m_importDir))         Directory.CreateDirectory(m_importDir);       if(!Directory.Exists(m_archiveDir))         Directory.CreateDirectory(m_archiveDir);       m_watchThread = new System.Threading.Thread(           new System.Threading.ThreadStart(ThreadStart));       m_watchThread.Start();     }     public void Shutdown()      {       m_watcher.EnableRaisingEvents = false;       m_watcher     = null;       m_watchThread = null;     }     protected void Initialize()     {       string[] files = System.IO.Directory.GetFiles(m_importDir, "*.*");       for(int i=0; i<files.Length; i++)       {         ProcessFile(files[i]);       }     }     protected void WatchFiles(string directory)     {       if(System.IO.Directory.Exists(directory))       {         m_watcher = new FileSystemWatcher();         m_watcher.Path          = directory;         m_watcher.NotifyFilter =          NotifyFilters.FileName | NotifyFilters.Attributes |          NotifyFilters.LastAccess | NotifyFilters.LastWrite |          NotifyFilters.Security | NotifyFilters.Size;         m_watcher.Changed += new FileSystemEventHandler(OnChanged);         m_watcher.EnableRaisingEvents = true;       }     }     protected void ThreadStart()     {       try       {         // Notifications will not occur if files already exist,         // so process the ones// already in the directory.         Initialize();         // Monitor the Import Directory.         WatchFiles(m_importDir);       }       catch(Exception e)       {         System.Diagnostics.Debug.WriteLine(e.Message);       }     }     protected void ProcessFile(string filePath)     {       try       {         // Because of multiple notifications, we might have already processed the         //file.         // Check to see if it still exists.         if(System.IO.File.Exists(filePath))         {           string newFileName = filePath;           newFileName = newFileName.Replace(m_importDir, m_archiveDir);           if(!System.IO.File.Exists(newFileName))           {             // Perform some processing on the file here.             System.Console.WriteLine("Moving {0} to the archive directory.", filePath);             // Now move the file.             System.IO.File.Move(filePath, newFileName);           }           else           {             System.Diagnostics.Debug.WriteLine("Could not process the file " + filePath +  " because it already exists in the archive directory.");           }         }       }       catch(Exception e)       {         System.Diagnostics.Debug.WriteLine(e.Message);       }     }     public void OnChanged(object source, FileSystemEventArgs e)     {       // The file has changed, so process it now.       ProcessFile(e.FullPath);     }     [STAThread]     static void Main(string[] args)     {       FileSystemWatcherExample fwe =         new FileSystemWatcherExample(@"c:\import", @"c:\archive");       // Wait until the user presses the return key before exiting.       System.Console.ReadLine();       fwe.Shutdown();     }   } } 


In most situations, the FileSystemWatcher is an extremely powerful and useful tool, enabling you to monitor events that occur in the file system. However, depending on the environment being monitored, various factors and other third-party software packages can cause this component to delay notifications, or even completely fail to receive notifications. If you need mission-critical, accurate timing notifications, you might find that the FileSystemWatcher doesn't perform as well as needed.

    Visual C#. NET 2003 Unleashed
    Visual C#. NET 2003 Unleashed
    ISBN: 672326760
    EAN: N/A
    Year: 2003
    Pages: 316 © 2008-2017.
    If you may any questions please contact us: