A stream is a sequence of bytes.
There is an abstract class named Stream
that represents a stream. There are many classes that inherit from this base class, so they all work the same way. In the following table are some of the common members of the Stream
class:
Member |
Description |
|
This determines whether you can read from and write to the stream |
|
This determines the total number of bytes and the current position within the stream |
|
This closes the stream and releases its resources |
|
If the stream has a buffer, then it is cleared and written to the underlying stream |
|
This reads a specified number of bytes from the stream into a byte array and advances the position |
|
This reads the next byte from the stream and advances the position |
|
This moves the position to the specified position (if CanSeek is true) |
|
This writes the contents of a byte array into the stream |
|
This writes a byte to the stream |
Storage streams can be read and written to, and the bytes will be stored in that location.
Function streams can only be "plugged onto" other streams to add functionality.
Namespace |
Class |
Description |
|
|
This encrypts and decrypts the stream |
|
|
This compresses and decompresses the stream |
|
|
This sends credentials across the stream |
Although there will be occasions where you need to work with streams at a low level, most often, you can plug helper classes into the chain to make things easier. Here are some helper classes to handle common scenarios:
Add a new console application project named Ch10_Streams
.
In Visual Studio 2017, set the solution's start-up project to be the current selection.
Import the System.IO
and System.Xml
namespaces, statically import the System.Console
type, and add the following statement to the Main
method:
// define an array of strings string[] callsigns = new string[] { "Husker", "Starbuck", "Apollo", "Boomer", "Bulldog", "Athena", "Helo", "Racetrack" }; // define a file to write to using a text writer helper string textFile = @"/Users/markjprice/Code/Ch10_Streams.txt"; // string textFile = @"C:CodeCh10_Streams.txt"; // Windows StreamWriter text = File.CreateText(textFile); // enumerate the strings writing each one to the stream foreach (string item in callsigns) { text.WriteLine(item); } text.Dispose(); // close the stream // output all the contents of the file to the Console WriteLine($"{textFile} contains {new FileInfo(textFile).Length} bytes."); WriteLine(File.ReadAllText(textFile)); // define a file to write to using the XML writer helper string xmlFile = @"/Users/markjprice/Code/Ch10_Streams.xml"; // string xmlFile = @"C:CodeCh10_Streams.xml"; FileStream xmlFileStream = File.Create(xmlFile); XmlWriter xml = XmlWriter.Create(xmlFileStream, new XmlWriterSettings { Indent = true }); // write the XML declaration xml.WriteStartDocument(); // write a root element xml.WriteStartElement("callsigns"); // enumerate the strings writing each one to the stream foreach (string item in callsigns) { xml.WriteElementString("callsign", item); } // write the close root element xml.WriteEndElement(); xml.Dispose(); xmlFileStream.Dispose(); // output all the contents of the file to the Console WriteLine($"{xmlFile} contains {new FileInfo(xmlFile).Length} bytes."); WriteLine(File.ReadAllText(xmlFile));
Run the console application and view the output:
C:CodeCh10_Streams.txt contains 68 bytes. Husker Starbuck Apollo Boomer Bulldog Athena Helo Racetrack C:CodeCh10_Streams.xml contains 320 bytes. <?xml version="1.0" encoding="utf-8"?> <callsigns> <callsign>Husker</callsign> <callsign>Starbuck</callsign> <callsign>Apollo</callsign> <callsign>Boomer</callsign> <callsign>Bulldog</callsign> <callsign>Athena</callsign> <callsign>Helo</callsign> <callsign>Racetrack</callsign> </callsigns>
XML is relatively verbose, so it takes up more space in bytes than plain text. We can squeeze the XML using a common compression algorithm known as GZIP.
Import the following namespace:
using System.IO.Compression;
Add the following code to the end of the Main
method:
// compress the XML output string gzipFilePath = @"/Users/markjprice/Code/Ch10.gzip"; // string gzipFilePath = @"C:CodeCh10.gzip"; // Windows FileStream gzipFile = File.Create(gzipFilePath); GZipStream compressor = new GZipStream(gzipFile, CompressionMode.Compress); XmlWriter xmlGzip = XmlWriter.Create(compressor); xmlGzip.WriteStartDocument(); xmlGzip.WriteStartElement("callsigns"); foreach (string item in callsigns) { xmlGzip.WriteElementString("callsign", item); } xmlGzip.Dispose(); compressor.Dispose(); // also closes the underlying stream // output all the contents of the compressed file to the Console WriteLine($"{gzipFilePath} contains {new FileInfo(gzipFilePath).Length} bytes."); WriteLine(File.ReadAllText(gzipFilePath)); // read a compressed file WriteLine("Reading the compressed XML file:"); gzipFile = File.Open(gzipFilePath, FileMode.Open); GZipStream decompressor = new GZipStream(gzipFile, CompressionMode.Decompress); XmlReader reader = XmlReader.Create(decompressor); while (reader.Read()) { // check if we are currently on an element node named callsign if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "callsign")) { reader.Read(); // move to the Text node inside the element WriteLine($"{reader.Value}"); // read its value } } reader.Dispose(); decompressor.Dispose();
Rerun the application and notice that the compressed XML is less than half the size of the same XML without compression:
C:CodeCh10.gzip contains 150 bytes. ▼ ♦ {?{??}En?BYjQqf~???????Bj^r~Jf^??RiI??????MrbNNqfz^1?i?QZ??Zd?☼↨@H♣?$ ▬%? &gc?t,?????*????H?????t?&?d??%b??H?aUPbrjIQ"?←?◄?♦ ??9→∟:) Reading the compressed XML file: Husker Starbuck Apollo Boomer Bulldog Athena Helo Racetrack
3.146.178.165