10.2 Encapsulating Raw Streams
While the Stream class reads and writes raw bytes, most programs prefer
to produce and consume data either in the form of native data types
or lines of text. To make this easy, the framework includes related
pairs of XXXReader/XXXWriter
classes that provide this higher-level, encapsulated access to the
raw underlying data stream.
10.2.1 The BinaryReader and BinaryWriter Classes
BinaryReader and BinaryWriter
are
concrete
classes that define operations for reading and writing a stream of
native data types. The most fundamental operations of the
BinaryReader and BinaryWriter
classes are the methods that read and write instances of the
primitive data types: bool,
byte, char,
decimal, float,
double, short,
int, long,
sbyte, ushort,
uint, and ulong. Additionally,
methods are provided to read and write strings and
arrays of the primitive data types.
Imagine we have a simple class, defined as follows, that we want to
read and write from a stream:
public class Student {
public string Name;
public int Age;
public double GPA;
}
Methods that read and write instances of the
Student class from a stream in a binary format
might look like this:
void SaveToStream(Stream stm, Student s) {
BinaryWriter bw = new BinaryWriter(stm);
bw.Write(s.Name);
bw.Write(s.Age);
bw.Write(s.GPA);
bw.Flush( ); // Ensure the BinaryWriter buffer is empty
}
void ReadFromStream(Stream stm, Student s) {
BinaryReader br = new BinaryReader(stm);
s.Name = br.ReadString( );
s.Age = br.ReadInt32( );
s.GPA = br.ReadDouble( );
}
10.2.2 The Abstract TextReader and TextWriter Classes
TextReader and TextWriter
are abstract base classes that define
operations for reading and writing a stream of characters. The most
fundamental operations of the TextReader and
TextWriter classes are the methods that read and
write a single character to or from a stream.
The TextReader class provides default
implementations for methods that read in an array of characters or a
string representing a line of characters. The
TextWriter class provides default implementations
for methods that write an array of characters, as well as methods
that convert common types (optionally with formatting options) to a
sequence of characters.
The framework includes a number of different concrete implementations
of the abstract base classes TextReader and
TextWriter. Some of the most prominent include
StreamReader and StreamWriter,
and StringReader and
StringWriter.
10.2.3 The StreamReader and StreamWriter Classes
StreamReader and StreamWriter
are
concrete classes that derive from TextReader and
TextWriter, respectively, and operate on a
Stream that may be passed as a constructor
parameter.
These classes allow you to combine a Stream (which
can have a backing store but knows only about raw data) with a
TextReader or TextWriter (which
knows about character data, but doesn't have a
backing store).
In addition, StreamReader and
StreamWriter can perform special translations
between characters and raw bytes. Such translations include
translating Unicode characters to ANSI characters to either
big-endian or little-endian format.
Assuming the existence of a Student class as
defined in the previous example, methods to read and write instances
of the Student class to/from a stream in a binary
format might look like this:
void SaveToStream(Stream stm, Student s) {
TextWriter tw = new StreamWriter(stm);
tw.WriteLine(s.Name);
tw.WriteLine(s.Age);
tw.WriteLine(s.GPA);
tw.Flush( ); // Ensure the TextWriter buffer is empty
}
void ReadFromStream(Stream stm, Student s) {
TextReader tr = new StreamReader(stm);
s.Name = tr.ReadLine( );
s.Age = Int32.Parse(tr.ReadLine( ));
s.GPA = Double.Parse(tr.ReadLine( ));
}
10.2.4 The StringReader and StringWriter Classes
StringReader and StringWriter
are
concrete
classes that derive from
TextReader and TextWriter,
respectively, and operate on a string or
StringBuilder, which may be passed in as a
constructor parameter.
The StringReader class can be thought of as the
simplest possible read-only backing store, because it simply performs
read operations on the underlying string. Also, the
StringWriter class can be thought of as the
simplest possible write-only backing store, because it simply
performs write operations on that StringBuilder.
The following example uses a StringWriter wrapped
around an underlying StringBuilder backing store
to write to a string:
using System;
using System.IO;
using System.Text;
class Test {
static void Main( ) {
StringBuilder sb = new StringBuilder( );
StringWriter sw = new StringWriter(sb);
WriteHello(sw);
Console.WriteLine(sb);
}
static void WriteHello(TextWriter tw) {
tw.Write("Hello, String I/O!");
}
}
|