File and Stream I/OComputers 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/OA 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 www.dictionary.com, 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 ClassThe 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 Exampleusing 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 The FileInfo ClassThe 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. TIP 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 Exampleusing 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 ClassWhereas the File and FileInfo classes work on individual files, the Directory class provides methods to copy, move, rename, create, and delete directories Listing 7.3. Using the Directory Classusing System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 { class DirectoryExample { [STAThread] static void Main(string[] args) { string initialDirectory = @"c:\InitialDirectory"; string moveDirectoryHere = @"c:\DirectoryMovedHere"; Using Streams: FileStream, MemoryStream, StringReader, and StringWriterAll 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 ClassThe 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 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 Classusing System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 { class FileStreamExample { static public void OutputFile(string filePath) { FileInfo fileInfo = new FileInfo(filePath); if(fileInfo.Exists) { Figure 7.1. Add the command-line argument here when testing the application.![]() The MemoryStream ClassThe 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. TIP 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. MemoryStreamExampleusing 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 ClassesThe 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. StringReaderWriterExampleusing 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 StreamWriterThe 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 Listing 7.7. StreamReaderWriterExampleusing System; using System.IO; namespace SAMS.VisualCSharpUnleashed.Chapter7 { class StreamReaderWriterExample { static public void CreateFile(string filePath) Using the FileSystemWatcherUntil 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.
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 Listing 7.8. FileSystemWatcher Exampleusing 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() TIP 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. |