15.1. Streams

C#'s core I/O classes and interfaces are defined in the System.IO namespace. The System.IO.Stream class is an abstract class that abstracts out the concept of a stream as merely a sequence of bytes. A stream can be read from or written to synchronously or asynchronously depending on the method call you use. A stream of bytes is usually associated with the underlying medium on which I/O operations such as a read/write are carried out. The medium can be an I/O device, a disk file, memory, or a TCP/IP socket. Java has a similar concept of streams (java.io.InputStream and java.io.OutputStream). The C# System.IO.Stream class is somewhat equivalent to the Java classes, although it does not have separate classes for reading and writing; instead, it has CanRead and CanWrite properties that indicate which operation can be carried out on the stream.

There are certain basic operations that can be carried out on all subclasses of the System.IO.Stream class. Table 15.1 shows a subset of these operations that are obvious to Java programmers.

Table 15.1. Core Operations of the System.IO.Stream Class
ClassDescription
ReadReads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
[Serializable]
public abstract int Read(
  in byte[] buffer,
  int offset,
  int count
);

WriteWrites a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
CloseCloses the current stream and releases any system resources associated with the stream.
FlushWrites the underlying stream's buffered data to be written to the underlying device the stream is abstracting.
SeekSets the position within the current stream.

Table 15.2. Equivalent Java and C# I/O Methods
Java ClassJava MethodEquivalent C# System.IO.Stream Method
java.io.InputStreamread()Read()
java.io.OutputStreamwrite()Write()
java.io.InputStreamclose()Close()
java.io.OutputStreamclose()Close()
java.io.OutputStreamflush()Flush()
java.io.InputStreammark, markSupported(), reset()Seek()

Note that the System.IO.Stream class has both the Read() and Write() methods because it can function as both an input stream and an output stream.

Table 15.2 shows the equivalency between Java's I/O class methods and C#'s System.IO.Stream methods.

The C# Stream class has the Length and Position properties, which make it easy to determine your progress in accessing (reading or writing) the stream. In Java, the onus of keeping track of the position inside the stream is on the programmer.

You can use the CanSeek, CanRead, and CanWrite properties to decide whether you are dealing with an input or an output operation. Listing 15.1 shows the Stream class in action.

Listing 15.1. Using the System.IO.Stream Class (C#)
using System;
using System.IO;

public class Test {

  public static void Main(string[] args) {

    Stream s = null;
    try {
      s = new MemoryStream();
      WriteStream(s);
      ReadStream(s);
      BulkWriteStream(s);
      BulkReadStream(s);
      SeekStream(s);
    } finally {
      s.Close();
    }
  }
  private static void WriteStream(Stream s) {
    for (int i = 0; i < 100; ++i) s.WriteByte((byte)i);
  }
  private static void ReadStream(Stream s) {
    s.Position = 0;
    int i = 0;
    while ((i = s.ReadByte()) != 1) {
        Console.WriteLine(i);
    }
  }
  private static void BulkReadStream(Stream s) {
    s.Position = 0;
    byte[] arr = new byte[s.Length];
    int bytesRead = 0;
    int bytes2Read = (int) s.Length;
    while (bytesRead > 0) {
      int val = s.Read(arr,bytesRead, bytes2Read);
      if (val == 0) break;
      bytes2Read = val;
      bytesRead += val;
    }
  }
  private static void BulkWriteStream(Stream s) {
      byte[] bytes = new byte[100];
      s.Write(bytes,0, 100);
      s.Flush();
  }
  private static void SeekStream(Stream s) {
    long start = 1;
    while (s.Position != 1){
      long position = s.Seek(start, SeekOrigin.End);
      Console.WriteLine(s.ReadByte());
      ——start;
    }
  }
}

In ReadStream() and WriteStream() we read and write one byte at a time. In BulkReadStream and BulkWriteStream we read and write as much as we can using byte[].

Note how the Position property is used to determine the position in the stream. After the first method call (WriteStream), the position is 100. This must be reset to 0 so that ReadStream() can read the stream from the beginning. Note that in BulkReadStream the Read(byte[], int, int) method may not read all the bytes at once, and hence we must keep track of how many bytes are read and how many are to be read.

In the last method call, SeekStream(), we try to go backwards in the Stream. We use C#'s Seek() method call to initially position the stream pointer at the end of the Stream. We use the SeekOrigin enumeration to specify where we want to position the pointer. After the pointer is positioned, we read one byte at a time. Here, the ReadByte() method moves one byte in the opposite direction until we reach the beginning of the Stream.

The four subclasses of the Stream class are FileStream, Buffered Stream, MemoryStream, and NetworkStream. Let's look at each subclass in turn.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.70.255