Streams in C#

Every software application consists of three major parts, an input unit, a processing unit, and an output unit. The software can take input from a keyboard, socket, network, file, memory object, etc. Similarly, applications can output processed results to devices such as a monitor screen, a file, socket or network. From the input-process-output cycle above, it is safely assumed that software needs to receive data at one point and send data to the other. Streams allow developers to perform this point to point communication in C# .NET.

In C#, streams refer to the group of classes that are:

  1. Used to read different types of data such as bytes, binary data, strings, characters from files, sockets, keyboard, etc.
  2. Used to write different data in the form of bytes, binary data, strings and characters to files, sockets, and other output devices.

Basically, all the I/O functions in C# are performed using Streams. Almost all of the C# Stream classes belong to System.IO namespace.

It is crucial to mention here that System.IO.Stream itself is an abstract class and only serves as base class for the other stream classes such as FileStream, StreamReader, StreamWriter, etc. Any class that wants to implement I/O functions must derive itself from the Stream class.

Buffer Access

Buffer Access classes are used to access and read data stored in the buffer. StreamReader, StreamWriter, BinaryReader, BinaryWriter, StringReader, StringWriter, and FileStream are some of the examples of classes that can access memory buffer.

As an example, let’s have a look at how FileStream class is initialized. Along with how it reads content from one file and writes that content to the other file via a byte array.  Consider the following code snippet.

string inputPath = @"E:\input.txt";
string outputPath = @"E:\output.txt";

try
{
    using (FileStream fsInput = new FileStream(inputPath, FileMode.Open, FileAccess.Read))
    {
        // Byte array for storing contents of file
        byte[] inputArray = new byte[fsInput.Length];
        int numOfBytes = (int) fsInput.Length;
        int totalBytesRead = 0;
        while (numOfBytes > 0)
        {
            int nbs = fsInput.Read(inputArray, totalBytesRead, numOfBytes);
            if (nbs == 0)
            {
                break;
            }

            totalBytesRead += nbs;
            numOfBytes -= nbs;
        }
        numOfBytes = inputArray.Length;

        // Output filestream for writing byte array
        using (FileStream fsOutput = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
        {
            fsOutput.Write(inputArray, 0, numOfBytes);
        }
    }
}
catch
{
    throw new Exception();
}

Now pay close attention to the code above. In the above code, we first initialized a FileStream object named “fsInput”. Notice that FileStream constructor takes three arguments. First one is the path to the file which you want to access. The second parameter is the mode in which you want to access the file. In the above code, the mode for fsInput is FileMode.Open. The third parameter is the type of function that you want to perform on the file. Since we are opening the file for reading, we passed FileAccess.Read as the third parameter.

Now before proceeding further, you must have a file named “input.txt” in the root of your “E:/” directory. You can add any text to the file. It’s up to you.

In proceeding lines of code, a byte array the size of the fsInput object is initialized. Next, inside a while loop,   the Read method is called on the fsInput object. The Read method takes three arguments: the byte array into which you want to read the data, the offset which specifies the position from which the byte array will be read, and the number of bytes to read in the integer.

In the same iteration of the while loop, a new FileStream object fsOutput is initialized with FileMode.Create and FileAccess.Write as the second and the third parameter which means that this object will be used to create a file and write content to it.  Finally, the Write method is used called on the fsOutput object which writes the byte array into the file. The Write method is similar to the Read method regarding parameters.

It is important to note that this reading and writing task is performed during each iteration. Therefore as soon as the data is read from the input file, it is written to the output file.

Directory Operations

Directory operations classes are used to perform different directory functions such as reading names of all files in the directory, creating a new file inside a directory, removing a directory, copying files from one directory to the other, etc. Some of the most widely used classes are File, Directory, FileInfo, DirectoryInfo, etc.

Let’s have a look at the following code sample. This code creates a new directory in the root of “E:/” drive.

DirectoryInfo newdir = new DirectoryInfo(@"E:\New Sample Directory");
if (newdir.Exists)
{
    Console.WriteLine("Directory with the same name exists");
    return;
}

    newdir.Create();
    Console.WriteLine("Your directory has been created.");

In the above code, an object of DirectoryInfo class is initialized. The path to the directory in which you want to create is passed as a parameter to the constructor of DirectoryInfo class. Next, using “Exists” property, it is checked whether or not such directory exists. If the directory exists, the program returns without creating the directory. However, if the directory doesn’t already exist, the “Create” method of the DirectoryInfo class is used to create the directory.

Similarly, if you want to delete a directory, you simply call “Delete” function on the object of the DirectoryInfo class.

It is important to note the difference between Directory and DirectoryInfo classes. The Directory class is a static class that allows basic one-off directory operations. The DirectoryInfo class is an instance class and is used where you want to perform directory operations repeatedly.

FileInfo is another instance type class and is used to get information about any file. Take a look at the following example to see how FileInfo class works.

FileInfo newFile = new FileInfo(@"E:\Sample2.txt");
Console.WriteLine(newFile.CreationTime);
Console.WriteLine(newFile.LastAccessTime);

In the above code, an object of FileInfo class named “newFile” is initialized. The path to the file is passed as a parameter to the newFile object.

Once you have a reference to the FileInfo object, you can get all sorts of information about the file. For instance, in the above code, “CreationTime” property returns the time of creation of the file. Similarly “LastAccessTime” returns the time at which the file was last accessed.

Like Directory, the File class is a static alternative to FileInfo class. You can use File class to create a file handler which can then be used to read and write data to a class.

In this article, we discussed what different types of Stream classes are and how they are used in code to perform different input and output operations. In the next article, I will introduce you to another very important C# concept, Scope and Boxing.

As always, if you have any questions or comments, please leave a comment below or contact me via my contact page.