System I/O and Streams in C#

This article has been excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors of C# Corner.

A software system has three major components: input, output, and processing, as shown in Figure 6.1.

Figure6.1.gif

Figure 6.1: Software System Components

The input part of the system is responsible for accepting data in the form of bytes, streams, binary data, or strings from input devices such as keyboards, mouses, pads, or other media. In the processing component of a software system, you apply your logic on the data to produce some information. The output part of the system displays processed data through devices such as monitors or printers.

Input/output (I/O) is an important consideration when you design a system or computer application. All software applications must control input and output in some manner.

The .NET Framework provides a rich application program interface (API) to work with the system I/O. This API resides in the System.IO namespace.

Overview of System.IO Namespace

In the .NET Framework, the System.IO namespace defines classes for reading and writing files and data streams. The System.IO namespace, which resides in the mscorlib.dll assembly, provides classes for working with the system I/O and with streams.

The main functionality of the System.IO namespaces in .NET includes the following features:

  • Accessing Buffer: The .NET Framework defines classes for reading and writing bytes, multibytes, binary data, strings, and character data with the help of StreamReader, 110 C# Corner StreamWriter, BinaryReader, BinaryWriter, StringReader, and StringWriter classes. The FileStream class can be used for random file access.
     
  • File and Directory Operations: The classes in the System.IO namespaces provide functionality for creation, deletion, and manipulation of files and directories. You can use the File, FileInfo and the Directory, DirectoryInfo classes to do most of your file and directory manipulations. The FileSystemWatcher class is used to monitor the file system.
     
  • Performance Optimization: The MemoryStream and the BufferStream classes enhance the performance of read/write operations by storing data in memory.

System I/O

The Console Class

The Console class from the System namespace helps you with system I/O to the command prompt window or console (remember the Hello C# example). The System.IO namespace contains classes like BinaryReader/BinaryWriter, StreamReader/StreamWriter, and FileStream to process streams of different kinds. All these classes are contained within the mscorlib.dll assembly.

The Console class provides access to the three standard streams-standard input, standard output, and standard error-by way of Console.In, Console.Out, and Console.Error, respectively.

System Output

The Console class has two methods, Write and WriteLine, to display output to the console. The distinction between these two methods is that the WriteLine method adds a line terminator after the output so you don't have to manually add the /n character to go to the next console line. Java programmers can map this method to the System.out.println method.

Note: Even though Console.Out is the output stream, you can omit the Out. In other words, the simplified Console.Write/Console.WriteLine generates the same output as Console.Out.Write/ Console.Out.WriteLine, with less typing!

Both methods are overloaded to take a variety of primitive data types such as int, float, char, long, double, uint, ulong, and string, as well as other parameters such as object and char[].

If you are passing a class containing data other than the primitive types, then that class's ToString method is called and printed on the screen. If the class does not have an implementation of the ToString method, then the object class's ToString method is called (because all classes in C# are derived from the object class).

Let's look at an example of overriding the ToString method. Note that compiling program in Listing 6.1 will generate the warning The private field 'OneMem.i' is never used. You can ignore this warning.

Listing 6.1: Example of Overriding ToString()


using
System;

public
class OneMem
{
    int i = 10;
    public class
OneOver
    {
        int i = 10;
        public override string ToString()
        {
            return i.ToString();
        }
    }

    public class
OneUser
    {
        public static void Main()
        {
            OneMem F = new OneMem();
            OneOver B = new OneOver();
            Console.WriteLine(F);
//This will print "OneMem"
            Console.WriteLine(B);
//This will print "10"
            Console.ReadLine();
        }
    }
}

Output of above Listing 6.1:

outputListitng6.1.gif

In Listing 6.1, the classes OneMem and OneOver have just one integer field each, but OneOver also overrides the ToString method to return the value of the integer field. In the class OneUser, when the WriteLine method is called upon instance F of the OneMem class (which has no implementation of the ToString method), the Object class's ToString method is called. The Object class's ToString method prints the name of the class OneMem. On the other hand, when WriteLine is called on instance B, it uses the overridden ToString method of the OneOver class. In this particular implementation, the ToString method prints the value of the integer field.

There is one more overloaded style of these methods, which takes a format string and three objects as input parameters (although four objects are supported, it is not Common Language Specification compliant). This overloaded method gives you control over the format of the string representation of the objects written to the console.

In Listing 6.2, "The sum of {0} and {1} is {2}" is the format string. The brackets {0}, {1}, and {2} are replaced by the values from the variables, which come after the format string in serial order. Here, {0} is replaced by the value of i, {1} by the value of j, and {2} by the value of z.

Listing 6.2: Example of Using a Format String


using
System;

public
class FormatOut
{
    public static void Main()
    {
        int i = 10;
        int j = 5;
        int z = i + j;
        Console.Write("The sum of {0} and {1} is {2}", i, j, z);

       
//This will print
       
//The sum of 10 and 5 is 15
        Console.ReadLine();
    }
}


Output of above Listing 6.2:

outputListitng6.2.gif

System Input

Console.In, which provides the standard input stream, has two methods for taking input from the console: the Read and ReadLine methods. The Read method reads one character at a time from the console whereas the ReadLine method, as its name suggests, reads a line off the console. Just as in system output, in system input you can also omit the In and use Console.Read/Console.ReadLine instead of Console.In.Read/Console.ReadLine.

The Read method reads a single character and returns an integer value, which represents the Unicode value of the character. The Read method does not return a Unicode value until the line is terminated by pressing the ENTER key. If there are no more characters to be read, it returns -1. Hence, if a user types more characters on the console before pressing ENTER, the first call to the 112 C# Corner Read method returns only the first character; and rest of the characters can be read with subsequent calls of the Read method.

Note: Pressing ENTER actually adds two characters at the end of the line: a carriage return (/r) and a line feed (/n). If you employ a loop using successive calls of the Read method, be sure to read the carriage return and line feed pair (with two additional reads) before the next Read call; otherwise, the next call will return /r instead of the user input.

The ReadLine method returns a string after reading a line from the console. A line ends with the first occurrence of a line terminator, such as /r or /n. In the case of ReadLine, the string returned does not contain the line terminator. If end of input is reached, then a null value is returned.

The example in Listing 6.3, although very simple, highlights some of the important features of system I/O. The purpose of the program is to display the number of words in a string that a user has entered.

Listing 6.3: Example of Reading Character Input from the Console


using
System;

public
class WordCount
{
    public static void Main()
    {
        Console.WriteLine("Welcome to the Word Count Program");
        bool con = true;
        while (con)
        {
            Console.Write("Enter a String :");

           
//Read a string from the user
            string original = Console.ReadLine();

           
//Remove any white spaces at the end of the sentence
            string trimmed = original.Trim();
            char[] sp = { ' ' };

           
//Split the string at every occurrence of a white space
            string[] str = trimmed.Split(sp);

           
//Print the word count using a formatted string
            Console.WriteLine("The word count is {0} for the string \"{1}\" ", str.Length, original);

           
//Leave one line blank on the screen
            Console.WriteLine();

           
//Ask the user if he wants to continue
            Console.Write("Do you want to continue [n]?");

           
//Read a single character 'y' or 'n' or 'N'
            int ans = Console.Read();

           
//Read the carriage return '/r'
            int carriage = Console.Read();

           
//Console.WriteLine("The value of carriage is "+carriage) ;
           
//13
           
//Read the line feed '/n'
            int linefeed = Console.Read();

           
//Console.WriteLine("The value of h is "+linefeed) ;//10
           
//Check if the user has put 'n' or 'N'
           
//We use the Unicode value for the characters 'n' and 'N'

            if (ans == 110 || ans == 78)
            {
               
//Set the bool variable to false so we exit the loop
                con = false;
            }
        }
        Console.WriteLine("Thank you for using my program !");
        Console.WriteLine("Press Enter to exit");

       
//Wait for the user to press Enter to exit
        Console.ReadLine();
    }
}


Output of above Listing 6.3:

outputListitng6.3.gif

The Main method of class WordCount (shown in Listing 6.3) takes a sentence as input from the user. Then it removes any trailing white spaces at the end of the sentence and finally breaks up the sentence into an array of strings using the Split method of the String class. The length of this array denotes the number of words in the sentence on the console.

Next, the user is given an option to continue or quit the program. A single character is read off the console and checked for its value. User input of 'n' or 'N' indicates a need to discontinue the while loop. We then set the condition flag to false, which causes the program to exit the loop.

You might note, as previously mentioned, the use of two Read methods after reading the input for the user to continue. This is due to the fact that the Read method returns when the ENTER key is pressed. Pressing ENTER has two effects: (1) the Read method returns, and (2) a carriage return (/r) and a line feed (/n) are appended to the console. Therefore, you read these two characters from the console input stream before calling the next ReadLine method.

As an aside, here's an observation and a handy tip. Many new programmers wonder why clicking on the console application from Windows Explorer causes the console screen to close quickly before they can see any output on the console screen. The solution to this problem is simple. Just place a ReadLine statement at the end of your code; the next time you run your program, the console screen does not close until ENTER is pressed.

System Error

System.Error is used to display error messages on the console. It also uses the Write and the WriteLine methods. You might wonder why you would use a separate stream to display errors if you can display errors using Console.Out. When you develop applications that use the SetOut method of the Console class to redirect the output of Console.Out to some other stream, then errors also need to be redirected to a separate stream. The Console.Error class provides a means of accomplishing this. Suppose you create an installer application that runs on the command line. You want the log file to record the success or failure of the component being installed, so you redirect Console.Out to a stream that writes to a log file. Thus, the user sees no output on the console. However, some missing files cause an exception to be thrown and you want to inform the user about it. You use the Console.Error stream to notify the user of the missing files on the console screen.

You might also wonder why you need the In and Out classes at all if you don't need to specify the In or Out during input or output. In and Out allow you to differentiate between different streams when you use the System.Error class. Because the Error stream uses the same methods as those of the Output stream, it's better to use the full name of the Output stream in such cases.

Conclusion

Hope this article would have helped you in understanding System I/O and Streams in C#. See other articles on the website on .NET and C#.


The Complete Visual C# Programmer's Guide covers most of the major components that make up C# and the .net environment. The book is geared toward the intermediate programmer, but contains enough material to satisfy the advanced developer.

visual C-sharp.jpg


Similar Articles
C# Corner
C# Corner started as an online community for software developers in 1999.