Recipe12.5.Choosing a Method of Opening a File or Stream for Reading andor Writing


Recipe 12.5. Choosing a Method of Opening a File or Stream for Reading and/or Writing

Problem

When you are first learning the .NET Frameworkand even for some time afterthe 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?

Solution

Use file streams to perform various file functions. There are five basic types of built-in file-stream manipulation classes that you can use to read from and/or write to a file stream:


FileStream

For the most fine-grained control, use FileStream for file manipulation since it provides the most low-level access to the file, and, therefore, the most complex actions become available. Some of these actions are reading and writing files in both synchronous and asynchronous fashions and methods to lock and unlock part or all of a file, seek a particular position in a file, or even read the file as a stream of either characters or bytes.


StreamReader

This type is derived from the abstract base class Textreader. The StreamReader class is designed for reading character or string input from a file. This class contains methods to read single characters, blocks of characters, lines of characters, or even the whole file into a single string variable.


StreamWriter

This class derives from the TextWriter class. It is designed for writing character or string output to a file. This class contains methods to write single characters or lines of characters.


BinaryReader

This type is derived from the Object class, as is the BinaryWriter class. It is designed for reading primitive data typesincluding byte or char datafrom a file. This class contains methods to read any of the simple types (int, long, float, etc.), including char arrays and byte arrays.


BinaryWriter

This type derives from the Object class. It is designed for writing primitive data typesincluding byte or char datato a file. This class contains methods to write any of the primitive types (int, long, float, etc.), including char arrays and byte arrays.

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.

Example 12-1 shows a few ways of using the various built-in streams.

Example 12-1. Using built-in .NET streams

 // Create a temp file to work with. string tempFile = Path.GetTempFileName( );  // FileStream // Open the file. using (FileStream 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); }  // StreamReader using (StreamReader 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); }  // StreamWriter StreamWriter streamWriter = null; // Open for append. streamWriter = new StreamWriter(tempFile,true); // Append some text. streamWriter.WriteLine(", It's the StreamWriter!");  // BinaryWriter long pos = 0; int twentyFive = 25; // Start up the binaryWriter with the base stream from the streamWriter // since it is open. using (BinaryWriter binaryWriter = new BinaryWriter(streamWriter.BaseStream)) {     // Move to end.     pos = binaryWriter.Seek(0, SeekOrigin.End);     // Write out 25.     binaryWriter.Write(twentyFive); } // Cannot call Close on the streamWriter since the // using stmt on the binaryWriter causes the binaryWriter.Dispose // method to be called, which in turn calls Dispose on the internal // reference to the streamWriter object that was passed in to the  // binaryWriter's constructor.  // BinaryReader Using (StreamReader streamReader2 = new StreamReader(tempFile)) {     using (BinaryReader binaryReader = new BinaryReader(streamReader2.BaseStream))     {         //long pos = 0;         //int twentyFive = 25;         // 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");     } } 

Discussion

There 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 12.19.

The FileStream constructor can also accept a FileMode, FileAccess, and/or FileShare enumeration value. These enumeration values are defined in Tables 12-2, 12-3, and 12-4, respectively.

Table 12-2. FileMode enumeration values

Value name

Definition

Specifics

Append

Opens an existing file and prepares it to be written to, starting at the end of the file. If the file does not exist, a new zero-length file is created.

This value can be used only in tandem with the FileAccess.Write enumeration value; otherwise, an ArgumentException is thrown.

Create

Creates a new file. If the specified file exists, it is truncated.

If you do not wish to lose data, consider employing the CreateNew enumeration value instead. This value can be used only in tandem with the FileAccess.Write or FileAccess.ReadWrite enumeration values; otherwise, an ArgumentException is thrown.

CreateNew

Creates a new file.

An IOException is thrown if the file already exists. This prevents accidental data loss. This value can be used only in tandem with the FileAccess.Write or FileAccess.ReadWrite enumeration values; otherwise, an ArgumentException is thrown.

Open

Opens an existing file.

A FileNotFoundException is thrown if the file does not exist. Use OpenOrCreate if it is possible that the file might not already exist.

OpenCreate

Opens a file if it exists or creates a new one if it does not exist.

Consider using Open if you expect the file to always exist before it is opened. An ArgumentException is not thrown if this enumeration value is used in tandem with the FileAccess.Read enumeration value.

truncate

Opens an existing file and deletes all information in that file.

A FileNotFoundException is thrown if the file does not exist. This value can be used in tandem with the FileAccess.Write or FileAccess.ReadWrite enumeration values; otherwise, an ArgumentException is thrown.


Table 12-3. FileAccess enumeration values

Value name

Definition

Read

Allows data to only be read from a file.

ReadWrite

Allows data to be read from and written to a file. Same as FileAccess.Read |FileAccess.Write.

Write

Allows data to only be written to a file.


Table 12-3. FileShare enumeration values

Value name

Definition

Inheritable

Allows the file handle to be inherited by a child process.

None

The file cannot be accessed (read from or written to) or deleted by any other process.

Read

The file cannot be written to or deleted by this or any other process. It can be read from.

ReadWrite

The file can be read from or written to by this or any other process. The file still cannot be deleted while it is being shared in this mode. Same as using FileShare.Read | FileShare.Write.

Write

The file cannot be read from or deleted by this or any other process. It can be written to.


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 classof the ones discussed in this chapterthat 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 12-5 shows these methods, along with their equivalent FileStream object constructor parameters.

Table 12-4. Static methods of the File class and their equivalent FileStream constructor calls

Static methods in File class

Equivalent FileStream constructor call

FileStream fileStream =File.Create("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None);

FileStream fileStream =File.Open("File.txt");

FileStream fileStream = new FileStream("File.txt");

FileStream fileStream =File.OpenRead("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

FileStream fileStream =File.OpenWrite("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);


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.

The File class has a complementary class called FileInfo that contains similar methods, but these methods are instance, not static, methods. Table 12-6 shows the FileInfo methods, which are similar to the File static methods, along with their equivalent FileStream object constructor parameters.

Table 12-5. Instance methods of the FileInfo class and equivalent FileStream constructor calls

Instance methods in FileInfo class

Equivalent FileStream constructor call

FileInfo fileInfo =new FileInfo("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None);

FileStream fileStream =fileInfo.Create( );

 

FileInfo fileInfo = new FileInfo("File.txt");

FileStream fileStream = new FileStream("File.txt");

FileStream fileStream = fileInfo.Open(FileMode.open);

 

FileInfo fileInfo = new FileInfo("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

FileStream fileStream = fileInfo.OpenRead( );

 

FileInfo fileInfo = new FileInfo("File.txt");

FileStream fileStream = new FileStream("File.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);

FileStream fileStream = fileInfo.OpenWrite( );

 


The FileInfo.Open instance method is overloaded to accept FileMode, FileAccess, and FileShare enumeration values.

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 12-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 12-6. Static methods of the File class and their equivalent StreamReader/StreamWriter constructor calls

Static methods in File class

Equivalent StreamReader/StreamWriter constructor calls

StreamReader streamReader = File.OpenText("File.txt");

StreamReader streamReader = new StreamReader("File.txt");

StreamWriter streamWriter = File.AppendText("File.txt");

StreamWriter streamWriter = new StreamWriter("File.txt", true);

StreamWriter streamWriter = File.CreateText("File.txt");

StreamWriter streamWriter = new StreamWriter("File.txt", false);


Table 12-8 describes the instance methods of the FileInfo class used to create StreamReader and StreamWriter objects and their equivalent StreamReader and StreamWriter object constructor parameters.

Table 12-7. Instance methods of the FileInfo class and their equivalent StreamReader/StreamWriter constructor calls

Instance methods in FileInfo class

Equivalent StreamReader/StreamWriter constructor calls

FileInfo fileInfo = new FileInfo("File.txt");

StreamReader streamReader = new StreamReader("File.txt");

StreamReader streamReader = fileInfo.OpenText( );

 

FileInfo fileInfo = new FileInfo("File.txt");

StreamWriter streamWriter = new StreamWriter("File.txt", true);

StreamWriter streamWriter = fileInfo.AppendText( );


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 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 Also

See Recipe 12.19; see the "FileStream Class," "StreamReader Class," "StreamWriter Class," "BinaryReader," and "BinaryWriter" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net