Home  Contents

Input & output

This chapter is dedicated to input & output in C#. The input & output in C# is based on streams.

A stream is an abstraction of a sequence of bytes, such as a file, an input/output device, an inter-process communication pipe, or a TCP/IP socket. Streams transfer data from one point to another point. Streams are also capable of manipulating the data; for example they can compress or encrypt the data. In the .NET Framework, the System.IO namespaces contain types that enable reading and writing on data streams and files.

The IDisposable interface

Streams implement the IDisposable interface. Objects that implement this interface must be disposed manually at the earliest opportunity. This is done by calling the Dispose() method in the finally block or by utilizing the using statement.

using System;
using System.IO;

public class ManualRelease
{
    static void Main() 
    {
        StreamReader sr = null;

        try
        {
            sr = new StreamReader("languages");
            Console.WriteLine(sr.ReadToEnd());

        } catch (IOException e)
        {
            Console.WriteLine("Cannot read file");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
            Console.WriteLine("Cannot access file");
            Console.WriteLine(e.Message);

        } finally 
        {
            if (sr != null) sr.Dispose();
        }
    }
}

In this example, we read characters from a file on a disk. We manually release allocated resources.

sr = new StreamReader("languages");
Console.WriteLine(sr.ReadToEnd());

The StreamReader class is used to read characters. Its parent implements the IDisposable interface.

} catch (IOException e)
{
    Console.WriteLine("Cannot read file");
    Console.WriteLine(e.Message);

} catch (UnauthorizedAccessException e)
{
    Console.WriteLine("Cannot access file");
    Console.WriteLine(e.Message);

Possible exceptions are handled in the catch blocks.

} finally 
{
    if (sr != null) sr.Dispose();
}

In the finally block, the Dispose() method cleans up the resources.

$ cat languages 
Python
Visual Basic
PERL
Java
C
C#
$ ./manualerelease.exe 
Python
Visual Basic
PERL
Java
C
C#

We have a languages file in the current directory. All lines of the file are printed to the console.

In the second example, we will use the using statement to automatically clean up resources.

using System;
using System.IO;

public class AutomaticCleanup
{
    static void Main() 
    {
        try
        {
            using (StreamReader sr = new StreamReader("languages"))
            {
                Console.WriteLine(sr.ReadToEnd());
            }            

        } catch (IOException e)
        {
            Console.WriteLine("Cannot read file");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
            Console.WriteLine("Cannot access file");
            Console.WriteLine(e.Message);
        }
    }
}

Again, we read the contents of the languages file.

using (StreamReader sr = new StreamReader("languages"))
{
    Console.WriteLine(sr.ReadToEnd());
}  

The using statement will automatically dispose the object specified between the parentheses. It will dispose the object even if an exception occurs.

MemoryStream

A MemoryStream is a stream which works with data in a computer memory.

using System;
using System.IO;

public class MemoryStreamExample
{
    static void Main()
    {
        try
        {
            using(Stream ms = new MemoryStream(6))
            {

                ms.WriteByte(9);
                ms.WriteByte(11);
                ms.WriteByte(6);
                ms.WriteByte(8);
                ms.WriteByte(3);
                ms.WriteByte(7);

                ms.Position = 0;

                int rs = ms.ReadByte();

                do 
                {
                    Console.WriteLine(rs);
                    rs = ms.ReadByte();                    

                } while (rs != -1);
            }

        } catch (IOException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

We write six numbers to a memory with a MemoryStream. Then we read those numbers and print them to the console.

using(Stream ms = new MemoryStream(6))

The line creates and initializes a MemoryStream object with a capacity of six bytes.

ms.WriteByte(9);
ms.WriteByte(11);
ms.WriteByte(6);
...

The WriteByte() method writes a byte to the current stream at the current position.

ms.Position = 0;

We set the position of the cursor in the stream to the beginning using the Position property.

do 
{
    Console.WriteLine(rs);
    rs = ms.ReadByte();                    

} while (rs != -1);

Here we read all bytes from the stream and print them to the console.

$ ./memorystream.exe 
9
11
6
8
3
7

This is the output of the memorystream.exe program.

StreamReader & StreamWriter

The StreamReader reads characters from a byte stream. It defaults to UTF-8 encoding. The StreamWriter writes characters to a stream in a particular encoding.

using System;
using System.IO;

public class ReadFile
{
    static void Main() 
    {     
       try
       {
           using(StreamReader stream = new StreamReader("languages"))
           {
               Console.WriteLine(stream.ReadToEnd());
           }

       } catch (IOException e)
       {
           Console.WriteLine("Cannot read file");
           Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access file");
           Console.WriteLine(e.Message);
        }  
    }
}

We have a file called languages. We read characters from that file and print them to the console.

using(StreamReader stream = new StreamReader("languages"))

The StreamReader takes a file name as a parameter.

Console.WriteLine(stream.ReadToEnd());

The ReadToEnd() method reads all characters to the end of the stream.

In the next example, we will be counting lines.

using System;
using System.IO;

public class CountingLines
{
    static void Main() 
    {
        int count = 0;

        try
        {           
           using (StreamReader stream = new StreamReader("languages"))
           {
               while(stream.ReadLine() != null)
               {
                   count++;
               }

               Console.WriteLine("There are {0} lines", count);
           } 

        } catch (IOException e)
        {
            Console.WriteLine("Cannot read file.");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access file");
           Console.WriteLine(e.Message);
        }
    }
}

We are counting lines in a file.

while(stream.ReadLine() != null)
{
    count++;
}

In the while loop, we read a line from the stream with the ReadLine() method. It returns a line from the stream or null if the end of the input stream is reached.

An example with StreamWriter follows. It is a class used for character output.

using System;
using System.IO;

public class CSharpApp
{
    static void Main() 
    {
        try 
        {
            using (MemoryStream ms = new MemoryStream())
            { 
                using (StreamWriter swriter = new StreamWriter(ms))
                {
                    swriter.Write("ZetCode, tutorials for programmers.");
                    swriter.Flush();
                            
                    ms.Position = 0;

                    using (StreamReader sreader = new StreamReader(ms))
                    {
                        Console.WriteLine(sreader.ReadToEnd());
                    } 
                }
            }

        } catch (IOException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

In the preceding example, we write characters to the memory and later we read them and print them to the console.

using (MemoryStream ms = new MemoryStream())

A MemoryStream is created. It is a stream whose backing store is memory.

using (StreamWriter swriter = new StreamWriter(ms))

A StreamWriter class takes a memory stream as a parameter. This way we are going to write characters to memory stream.

swriter.Write("ZetCode, tutorials for programmers.");
swriter.Flush();

We write some text to the writer. The Flush() clears all buffers for the current writer and causes any buffered data to be written to the underlying stream.

ms.Position = 0;

We set the current position within the stream to the beginning.

using (StreamReader sreader = new StreamReader(ms))
{
    Console.WriteLine(sreader.ReadToEnd());
} 

Now we create an instance of the stream reader and read everything we have previously written.

FileStream

A FileStream class uses a stream on a file on the filesystem. This class can be used to read from files, write to files, open them and close them.

using System;
using System.IO;
using System.Text;

public class FileStreamExample
{
    static void Main() 
    {
        try
        {
            using (FileStream fstream = new FileStream("author", FileMode.Append))
            {
                string text = "Фёдор Михайлович Достоевский";
                byte[] bytes = new UTF8Encoding().GetBytes(text);

                fstream.Write(bytes, 0, bytes.Length);
            }

        } catch (IOException e)
        {
            Console.WriteLine("Cannot write to file");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access file");
           Console.WriteLine(e.Message);
        }
    }
}

We write some text in Russian azbuka to the file called author in the current working directory.

using System.Text;

The UTF8Encoding class is located in the System.Text namespace.

using (FileStream fstream = new FileStream("author", FileMode.Append))

A FileStream object is created. The second parameter is a mode in which the file is opened. The append mode opens the file if it exists and seeks to the end of the file, or creates a new file.

string text = "Фёдор Михайлович Достоевский";

This is text in russian azbuka.

byte[] bytes = new UTF8Encoding().GetBytes(text);

An array of bytes is created from the text in russian azbuka.

fstream.Write(bytes, 0, bytes.Length);

We write the bytes to the file stream.

$ cat author 
Фёдор Михайлович Достоевский

We show the contents of the author file.

XmlTextReader

We can use streams to read xml data. The XmlTextReader is the class to read xml files in C#. The class is forward-only and read-only.

We have the following xml test file.

<?xml version="1.0" encoding="utf-8" ?>
<languages>
    <language>Python</language>
    <language>Ruby</language>   
    <language>Javascript</language>
    <language>C#</language>
</languages>

This file contains language names between custom XML tags.

using System;
using System.IO;
using System.Xml;

public class ReadingXMLFile
{
    static void Main() 
    {       
        string file = "languages.xml";
      
        try
        {
            using (XmlTextReader xreader = new XmlTextReader(file))
            {
                xreader.MoveToContent();

                while (xreader.Read())
                {
                    switch (xreader.NodeType)
                    {
                        case XmlNodeType.Element:
                            Console.Write(xreader.Name + ": ");
                            break;

                        case XmlNodeType.Text:
                            Console.WriteLine(xreader.Value); 
                            break;
                    }
                }
            } 
 
        } catch (IOException e)
        {
            Console.WriteLine("Cannot read file.");
            Console.WriteLine(e.Message);

        } catch (XmlException e)
        {
            Console.WriteLine("XML parse error");
            Console.WriteLine(e.Message);
        }
    }
}

This C# program reads data from the previously specified XML file and prints it to the terminal.

using System.Xml;

The System.Xml namespace contains classes related to Xml reading and writing.

using (XmlTextReader xreader = new XmlTextReader(file))

An XmlTextReader object is created. It is a reader that provides fast, non-cached, forward-only access to XML data. It takes the file name as a parameter.

xreader.MoveToContent();

The MoveToContent() method moves to the actual content of the XML file.

while (xreader.Read())

This line reads the next node from the stream. The Read() method returns false if there are no more nodes left.

case XmlNodeType.Element:
    Console.Write(xreader.Name + ": ");
    break;

case XmlNodeType.Text:
    Console.WriteLine(xreader.Value); 
    break;

Here we print the element name and element text.

} catch (XmlException e)
{
    Console.WriteLine("XML parse error");
    Console.WriteLine(e.Message);
}

We check for XML parse error.

$ ./readxml.exe 
language: Python
language: Ruby
language: Javascript
language: C#

This is the output of example.

Files and directories

The .NET framework provides other classes that we can use to work with files and directories.

A File class is a higher level class that has static methods for file creation, deletion, copying, moving and opening. These methods make the job easier.

using System;
using System.IO;

public class CreateFile
{
    static void Main() 
    {      
        try
        {
            using (StreamWriter sw = File.CreateText("cars")) 
            {
                sw.WriteLine("Hummer");
                sw.WriteLine("Skoda");
                sw.WriteLine("BMW");
                sw.WriteLine("Volkswagen");
                sw.WriteLine("Volvo");
            }

        } catch (IOException e)
        {
           Console.WriteLine("Cannot create file");
           Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access file");
           Console.WriteLine(e.Message);
        }
    }
}

In the example, we create a cars file and write some car names into it.

using (StreamWriter sw = File.CreateText("cars")) 

The CreateText() method creates or opens a file for writing UTF-8 encoded text. It returns a StreamWriter object.

sw.WriteLine("Hummer");
sw.WriteLine("Skoda");
...

We write two lines to the stream.

$ cat cars
Hummer
Skoda
BMW
Volkswagen
Volvo

We have successfully written five car names to a cars file.

In the second example, we show other five static methods of the File class.

using System;
using System.IO;

public class CopyFile
{
    static void Main() 
    {
       try
       {        
            if (File.Exists("cars"))
            {
                Console.WriteLine(File.GetCreationTime("cars"));
                Console.WriteLine(File.GetLastWriteTime("cars"));
                Console.WriteLine(File.GetLastAccessTime("cars"));
            }

            File.Copy("cars", "newcars");

       } catch (IOException e)
       {
           Console.WriteLine("IO error");
           Console.WriteLine(e.Message);
       }
    }
}

If a specified file exists, we determine its creation, last write and last access times. Then we copy the contents of the file to another file.

if (File.Exists("cars"))

The Exists() method determines whether the specified file exists.

Console.WriteLine(File.GetCreationTime("cars"));
Console.WriteLine(File.GetLastWriteTime("cars"));
Console.WriteLine(File.GetLastAccessTime("cars"));

We get creation time, last write time and last access time of the specified file.

File.Copy("cars", "newcars");

The Copy() method copies the file.

$ ./copyfile.exe 
8/31/2013 12:18:43 PM
8/31/2013 12:18:43 PM
8/31/2013 12:18:47 PM

This is a sample output.

The System.IO.Directory is a class that has static methods for creating, moving, and enumerating through directories and subdirectories.

using System;
using System.IO;

public class CreateDirectory
{
    static void Main() 
    {
        try
        {
            Directory.CreateDirectory("temp");
            Directory.CreateDirectory("newdir");
            Directory.Move("temp", "temporary");

        } catch (IOException e)
        {
            Console.WriteLine("Cannot create directory");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access directory");
           Console.WriteLine(e.Message);
        }
    }
}

We will use two methods from the above mentioned object. We create two directories and rename one of the created ones.

Directory.CreateDirectory("temp");

The CreateDirectory() method creates a new directory.

Directory.Move("temp", "temporary");

The Move() method gives a specified directory a new name.

The DirectoryInfo and Directory have methods for creating, moving, and enumerating through directories and subdirectories.

using System;
using System.IO;

public class ShowContents
{
    static void Main() 
    {
        try 
        {
            DirectoryInfo dir = new DirectoryInfo("test");

            string[] files = Directory.GetFiles("test");
            DirectoryInfo[] dirs = dir.GetDirectories();
            
            foreach (DirectoryInfo subDir in dirs)
            {
                Console.WriteLine(subDir.Name);
            }

            foreach (string fileName in files)
            {
                Console.WriteLine(fileName);
            }

        } catch (IOException e)
        {
            Console.WriteLine("IO exception");
            Console.WriteLine(e.Message);

        } catch (UnauthorizedAccessException e)
        {
           Console.WriteLine("Cannot access file");
           Console.WriteLine(e.Message);
        }        
    }
}

We use the DirectoryInfo class to traverse a specific directory and print its contents.

DirectoryInfo dir = new DirectoryInfo("test");

We will show the contents of this directory (test).

string[] files = Directory.GetFiles("test");

We get all files of the test directory using the static GetFiles() method.

DirectoryInfo[] dirs = dir.GetDirectories();

We get all directories.

foreach (DirectoryInfo subDir in dirs)
{
    Console.WriteLine(subDir.Name);
}

Here we loop through directories and print their names to the console.

foreach (string fileName in files)
{
    Console.WriteLine(fileName);
}

Here we loop through the array of files and print their names to the console.

$ ./showcontents.exe 
test/RayTracer.cs
test/RayTracer2.cs
test/apples.cs
test/beep.cs
test/comparenull.cs
...

This is a sample output of the example.

In this chapter, we have covered Input/Output operations in C#.