Reading
and writin
g data is accomplished with the
Stream
class. Remember streams? This is a chapter
about streams.[9]
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 |
---|---|
Abstract class that supports reading and writing bytes. | |
Read and write encoded strings and primitive datatypes to and from streams. | |
Provide implementations for the abstract
| |
For reading to and from | |
| |
A stream that adds buffering to another stream such as a
| |
A nonbuffered stream whose encapsulated data is directly accessible
in memory. A | |
A stream over a network connection. |
This
section will start 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: estsource est1.cs"); Stream outputStream = File.OpenWrite( @"C: estsource est1.bak");
To open the files to be read and written, you use the static
OpenRead()
and
OpenWrite()
methods of the File
class. The static overload of these methods takes as an argument the
path for the file, 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.
You 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 to be read.
while ( (bytesRead = inputStream.Read(buffer,0,SIZE_BUFF)) > 0 ) { outputStream.Write(buffer,0,bytesRead); }
Each buffer-full 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. You’ll 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: estsource est1.cs"); // the file to write to Stream outputStream = File.OpenWrite( @"C: estsource est1.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.
In
the previous example, you created a
buffer to read into. When you called Read
, a
buffer-full 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
, you start by
creating a normal stream class as you did in Example 21-4:
Stream inputStream = File.OpenRead( @"C: estsourcefolder3.cs"); Stream outputStream = File.OpenWrite( @"C: estsourcefolder3.bak");
Once you have the normal stream, you 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: estsourcefolder3.cs"); Stream outputStream = File.OpenWrite( @"C: estsourcefolder3.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.
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:
FileInfotheSourceFile = new FileInfo (@"C: estsource est1.cs"); StreamReader stream = theSourceFile.OpenText( );
OpenText()
returns a
StreamReader
for the file. With the
StreamReader
in hand, we 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, you call the
StreamWriter
constructor, passing in the full name
of the file you want to write to:
StreamWriter writer = new StreamWriter(@"C: estsourcefolder3.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: estsource est.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: estsource est.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 clas
s itself.
18.191.186.219