[ Team LiB ] |
Recipe 11.5 Choosing a Method of Opening a File or Stream for Reading and/or WritingProblemWhen you are first learning the .NET Framework—and even for some time after—the proper way to read to, write from, or otherwise interact with files can be unclear because the framework provides so many different ways of attacking this problem. How should you determine which approach fits your scenario? SolutionUse file streams to perform various file functions. There are five basic types of built-in file stream manipulation classes that you can use in order to read and/or write to the file stream:
There are other stream readers and writers (XmlTextReader/Writer, StringReader/Writer) that can also perform file stream functions but at a higher level. This recipe is meant to give you a more fundamental approach to file operations. Here are a few examples of using the various built-in streams: // create a temp file to work with string tempFile = Path.GetTempFileName( ); // FileStream FileStream fileStream = null; try { // open the file fileStream = File.Open(tempFile,FileMode.Append); string text = "Hello World "; byte [] bytes = Encoding.ASCII.GetBytes(text.ToCharArray( )); // write to the file fileStream.Write(bytes,0,bytes.Length); } finally { //make sure the file is closed if it was opened if(fileStream != null) fileStream.Close( ); } // StreamReader StreamReader streamReader = null; try { streamReader = new StreamReader(tempFile); char[] chars = new char[64]; // read a block of characters streamReader.Read(chars,0,64); string charsFound = new string(chars); Console.WriteLine("Chars in stream {0}",charsFound); } finally { if(streamReader != null) streamReader.Close( ); } // StreamWriter StreamWriter streamWriter = null; try { // open for append streamWriter = new StreamWriter(tempFile,true); // append some text streamWriter.WriteLine(", It's the StreamWriter!"); } finally { if(streamWriter != null) streamWriter.Close( ); } // BinaryWriter BinaryWriter binaryWriter = null; long pos = 0; int twentyFive = 25; try { // start up the binary writer with the base stream from the streamwriter // since it is open binaryWriter = new BinaryWriter(streamWriter.BaseStream); // move to end pos = binaryWriter.Seek(0, SeekOrigin.End); // write out 25 binaryWriter.Write(twentyFive); } finally { // close up if(binaryWriter != null) binaryWriter.Close( ); } // BinaryReader StreamReader streamReader2 = null; BinaryReader binaryReader = null; try { // open a new reader streamReader2 = new StreamReader(tempFile); binaryReader = new BinaryReader(streamReader2.BaseStream); // advance the stream to the number we stored for(long i=0;i<pos;i++) binaryReader.ReadByte( ); // read our number (should be 25) int num = binaryReader.ReadInt32( ); // is this the same number...? if(num == twentyFive) Console.WriteLine("Successfully read 25 back from stream"); else Console.WriteLine("Failed to successfully read 25 back from stream"); } finally { // close up if(binaryReader != null) binaryReader.Close( ); // close stream if(streamReader2 != null) streamReader2.Close( ); } DiscussionThere are many different ways to create a stream. First, we will examine the FileStream class, referring to useful recipes that will help create objects of this type. We will then look at the StreamWriter and StreamReader classes, followed by the BinaryWriter and BinaryReader classes. The most straightforward method of creating an object is to use the new keyword. The FileStream class has several overloaded class constructors that enable creating a new FileStream from scratch. The FileStream's constructor enables a new FileStream object to be created from either a filename or a file handle. See Recipe 11.19. The FileStream constructor can also accept a FileAccess, FileMode, and/or FileShare enumeration value. These enumeration values are defined in Tables Table 11-2, Table 11-3, and Table 11-4, respectively.
In addition to these enumerations that define how a file is opened, the FileStream constructor allows you to define whether this stream will be opened in a synchronous or asynchronous manner. This is the only class—of the ones discussed in this chapter—that allows a file to be opened in an asynchronous manner. The FileStream class also has methods for seeking to a point within a file stream, as well as locking or unlocking a portion or an entire file; locking will prevent other processes or threads from modifying the file. The other stream types discussed in this chapter do not have the ability to lock or unlock portions or an entire file. This locking/unlocking functionality cannot even be accessed through the BaseStream property of any of these types. Seeking within a file can be done directly using the BinaryReader or BinaryWriter classes. The StreamReader and StreamWriter classes cannot directly access the seek functionality. However, by using the BaseStream property of either the StreamReader or StreamWriter classes, the base stream's seek functionality can be used. FileStreams can also be created using the static methods of the File class. Table 11-5 shows these methods, along with their equivalent FileStream object constructor parameters.
The File.Open method is overloaded to accept FileMode, FileAccess, and FileShare enumeration values. The FileStream constructor is also overloaded to accept these same parameters. Therefore, to make an equivalent FileStream constructor for the File.Open method, we need to use the same parameters for each of these three enumeration values in both parameter lists. The File class has a complementary class called FileInfo that contains similar methods, but these methods are instance, not static, methods. Table 11-6 shows the FileInfo methods, which are similar to the File static methods, along with their equivalent FileStream object constructor parameters.
The FileInfo.Open instance method is overloaded to accept FileMode, FileAccess, and FileShare enumeration values. These values should be matched in the FileStream constructor parameter list. The StreamReader and StreamWriter objects can be created using their overloaded constructors. These overloaded constructors accept as parameters either a file path and name or a FileStream object. Therefore, we can use any of the previously mentioned ways of creating a FileStream object in the construction of either a StreamReader or StreamWriter object. In addition, we can use three of the static methods in the File class or three of the instance methods in the FileInfo class to create a StreamReader or StreamWriter object. Table 11-7 describes the static methods of the File class used to create StreamReader and StreamWriter objects and their equivalent StreamReader and StreamWriter object constructor parameters.
Table 11-8 describes the instance methods of the FileInfo class used to create StreamReader and StreamWriter object and their equivalent StreamReader and StreamWriter object constructor parameters.
The methods of the File and FileInfo classes do not return BinaryReader and BinaryWriter classes; therefore, we rely on their constructors to create these types of objects. The overloaded BinaryReader and BinaryWriter class constructors accept only a Stream object; they do not accept a filename. To create a BinaryReader or BinaryWriter object, we first need to create a Stream type object. Since Stream is an abstract class, we need to create one of its derived classes, such as the FileStream class. Any of the prior ways of creating a FileStream object may be employed as a parameter in the constructor of either a BinaryReader or BinaryWriter. The following code creates both a BinaryReader and a BinaryWriter object from a single FileStream object: fileStream = File.Create("filename.file"); BinaryWriter binaryWriter1 = new BinaryWriter(fileStream); BinaryReader binaryReader1 = new BinaryReader(fileStream); There are many different ways of combining the techniques discussed in this recipe to create and open files. For example, if you require file locking and/or asynchronous file processing, you will need a FileStream object. If you are dealing with text streams in memory and on disk, perhaps the StreamReader and StreamWriter might be a better choice. Finally, if you are dealing with binary data or mixed binary and text data in different encodings, you should consider BinaryReader and BinaryWriter. See AlsoSee Recipe 11.19; see the "FileStream Class," "StreamReader Class," "StreamWriter Class," "BinaryReader," and "BinaryWriter" topics in the MSDN documentation. |
[ Team LiB ] |