21.2 Reading and Writing Data

Reading and writing data is accomplished with the Stream class. Remember streams? This is a chapter about streams.[1]

[1] With a tip of the hat to Arlo Guthrie.

Stream supports synchronous and asynchronous reads and writes. The .NET Framework provides a number of classes derived from Stream, including FileStream, MemoryStream, and NetworkStream. In addition, there is a BufferedStream class, which provides buffered I/O and which can be used in conjunction with any of the other stream classes. The principal classes involved with I/O are summarized in Table 21-5.

Table 21-5. Principle I/O classes of the .NET Framework

Class

Use

Stream

Abstract class that supports reading and writing bytes.

BinaryReader/BinaryWriter

Read and write encoded strings and primitive datatypes to and from streams.

File, FileInfo, Directory, DirectoryInfo

Provide implementations for the abstract FileSystemInfo classes, including creating, moving, renaming, and deleting files and directories.

FileStream

For reading to and from File objects; supports random access to files. Opens files synchronously by default; supports asynchronous file access.

TextReader,TextWriter, StringReader,StringWriter

TextReader and TextWriter are abstract classes designed for Unicode character I/O. StringReader and StringWriter write to and from strings, allowing your input and output to be either a stream or a string.

BufferedStream

A stream that adds buffering to another stream such as a NetworkStream. Note that FileStream has buffering built in. BufferedStreams can improve performance of the stream to which they are attached.

MemoryStream

A nonbuffered stream whose encapsulated data is directly accessible in memory. A MemoryStream has no backing store, and is most useful as a temporary buffer.

NetworkStream

A stream over a network connection.

21.2.1 Binary Files

This section starts by using the basic Stream class to perform a binary read of a file. The term binary read is used to distinguish from a text read. If you don't know for certain that a file is just text, it is safest to treat it as a stream of bytes, known as a binary file.

The Stream class is chock-a-block with methods, but the most important are Read( ), Write( ), BeginRead( ), BeginWrite( ), and Flush( ). All of these are covered in the next few sections.

To perform a binary read, begin by creating a pair of Stream objects, one for reading and one for writing:

Stream inputStream = File.OpenRead(     @"C:\test\source\test1.cs"); Stream outputStream = File.OpenWrite(     @"C:\test\source\test1.bak");

To open the files to read and write, use the static OpenRead( ) and OpenWrite( ) methods of the File class. The static overload of these methods takes the path for the file as an argument, as shown previously.

Binary reads work by reading into a buffer. A buffer is just an array of bytes that will hold the data read by the Read( ) method.

Pass in the buffer, the offset in the buffer at which to begin storing the data read in, and the number of bytes to read. InputStream.Read reads bytes from the backing store into the buffer and returns the total number of bytes read.

It continues reading until no more bytes remain.

while ( (bytesRead =     inputStream.Read(buffer,0,SIZE_BUFF)) > 0 ) {     outputStream.Write(buffer,0,bytesRead); }

Each buffer-ful of bytes is written to the output file. The arguments to Write( ) are the buffer from which to read, the offset into that buffer at which to start reading, and the number of bytes to write. Notice that you write the same number of bytes as you just read.

Example 21-4 provides the complete listing.

Example 21-4. Implementing a binary read and write to a file
namespace Programming_CSharp {    using System;    using System.IO;    class Tester    {       const int SizeBuff = 1024;       public static void Main( )       {          // make an instance and run it          Tester t = new Tester( );          t.Run( );       }                // Set it running with a directory name       private void Run( )       {          // the file to read from          Stream inputStream = File.OpenRead(             @"C:\test\source\test1.cs");          // the file to write to          Stream outputStream = File.OpenWrite(             @"C:\test\source\test1.bak");          // create a buffer to hold the bytes           byte[] buffer = new Byte[SizeBuff];          int bytesRead;           // while the read method returns bytes          // keep writing them to the output stream          while ( (bytesRead =              inputStream.Read(buffer,0,SizeBuff)) > 0 )          {             outputStream.Write(buffer,0,bytesRead);          }          // tidy up before exiting          inputStream.Close( );          outputStream.Close( );       }    } }

The result of running this program is that a copy of the input file (test1.cs) is made in the same directory and named test1.bak.

21.2.2 Buffered Streams

In the previous example, you created a buffer to read into. When you called Read( ), a buffer-ful was read from disk. It might be, however, that the operating system can be much more efficient if it reads a larger (or smaller) number of bytes at once.

A buffered stream object allows the operating system to create its own internal buffer, and read bytes to and from the backing store in whatever increments it thinks is most efficient. It will still fill your buffer in the increments you dictate, but your buffer is filled from the in-memory buffer, not from the backing store. The net effect is that the input and output are more efficient and thus faster.

A BufferedStream object is composed around an existing Stream object that you already have created. To use a BufferedStream, start by creating a normal stream class as you did in Example 21-4:

Stream inputStream = File.OpenRead(     @"C:\test\source\folder3.cs"); Stream outputStream = File.OpenWrite(     @"C:\test\source\folder3.bak");

Once you have the normal stream, pass that stream object to the buffered stream's constructor:

BufferedStream bufferedInput =      new BufferedStream(inputStream); BufferedStream bufferedOutput =      new BufferedStream(outputStream);

You can then use the BufferedStream as a normal stream, calling Read( ) and Write( ) just as you did before. The operating system handles the buffering:

while ( (bytesRead =       bufferedInput.Read(buffer,0,SIZE_BUFF)) > 0 )  {      bufferedOutput.Write(buffer,0,bytesRead);  }

The only change is that you must remember to flush the buffer when you want to ensure that the data is written out to the file:

bufferedOutput.Flush( );

This essentially tells the operating system to take the entire contents of the in-memory buffer and write it out to disk.

Example 21-5 provides the complete listing.

Example 21-5. Implementing buffered I/O
namespace Programming_CSharp {    using System;    using System.IO;    class Tester    {       const int SizeBuff = 1024;       public static void Main( )       {          // make an instance and run it          Tester t = new Tester( );          t.Run( );       }                // Set it running with a directory name       private void Run( )       {          // create binary streams          Stream inputStream = File.OpenRead(             @"C:\test\source\folder3.cs");          Stream outputStream = File.OpenWrite(             @"C:\test\source\folder3.bak");          // add buffered streams on top of the          // binary streams          BufferedStream bufferedInput =              new BufferedStream(inputStream);          BufferedStream bufferedOutput =              new BufferedStream(outputStream);          byte[] buffer = new Byte[SizeBuff];          int bytesRead;          while ( (bytesRead =              bufferedInput.Read(buffer,0,SizeBuff)) > 0 )          {             bufferedOutput.Write(buffer,0,bytesRead);          }          bufferedOutput.Flush( );          bufferedInput.Close( );          bufferedOutput.Close( );       }    } }

With larger files, this example should run more quickly than Example 21-4 did.

21.2.3 Working with Text Files

If you know that the file you are reading (and writing) contains nothing but text, you might want to use the StreamReader and StreamWriter classes. These classes are designed to make manipulation of text easier. For example, they support the ReadLine( ) and WriteLine( ) methods that read and write a line of text at a time. You've used WriteLine( ) with the Console object.

To create a StreamReader instance, start by creating a FileInfo object and then call the OpenText( ) method on that object:

FileInfo theSourceFile =     new FileInfo (@"C:\test\source\test1.cs"); StreamReader stream = theSourceFile.OpenText( );

OpenText( ) returns a StreamReader for the file. With the StreamReader in hand, you can now read the file, line by line:

do {     text = stream.ReadLine( ); } while (text != null); 

ReadLine( ) reads a line at a time until it reaches the end of the file. The StreamReader will return null at the end of the file.

To create the StreamWriter class, call the StreamWriter constructor, passing in the full name of the file you want to write to:

StreamWriter writer = new StreamWriter(@"C:\test\source\folder3.bak",false);

The second parameter is the Boolean argument append. If the file already exists, true will cause the new data to be appended to the end of the file, and false will cause the file to be overwritten. In this case, pass in false, overwriting the file if it exists.

You can now create a loop to write out the contents of each line of the old file into the new file, and while you're at it, to print the line to the console as well:

do {     text = reader.ReadLine( );     writer.WriteLine(text);     Console.WriteLine(text); } while (text != null); 

Example 21-6 provides the complete source code.

Example 21-6. Reading and writing to a text file
namespace Programming_CSharp {    using System;    using System.IO;    class Tester    {       public static void Main( )       {          // make an instance and run it          Tester t = new Tester( );          t.Run( );       }                // Set it running with a directory name       private void Run( )       {          // open a file          FileInfo theSourceFile = new FileInfo(             @"C:\test\source\test.cs");          // create a text reader for that file          StreamReader reader = theSourceFile.OpenText( );          // create a text writer to the new file          StreamWriter writer = new StreamWriter(             @"C:\test\source\test.bak",false);          // create a text variable to hold each line          string text;          // walk the file and read every line          // writing both to the console          // and to the file          do          {             text = reader.ReadLine( );             writer.WriteLine(text);             Console.WriteLine(text);          } while (text != null);           // tidy up          reader.Close( );          writer.Close( );       }    } }

When this program is run, the contents of the original file are written both to the screen and to the new file. Notice the syntax for writing to the console:

Console.WriteLine(text);

This syntax is nearly identical to that used to write to the file:

writer.WriteLine(text);

The key difference is that the WriteLine( ) method of Console is static, while the WriteLine( ) method of StreamWriter, which is inherited from TextWriter, is an instance method, and thus must be called on an object rather than on the class itself.



Programming C#
C# Programming: From Problem Analysis to Program Design
ISBN: 1423901460
EAN: 2147483647
Year: 2003
Pages: 182
Authors: Barbara Doyle

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