C# input & output
last modified July 5, 2023
In this article we cover input & output in operations in C#. The input & output in C# is based on streams.
C# stream
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, the System.IO
namespaces contain types that enable
reading and writing on data streams and files.
C# provides higher-level methods for I/O operations in the File
class and lower-level methods in classes such as StreamReader
or StreamWriter
.
Handling exceptions
I/O operations are error-prone. We can run into exceptions such as
FileNotFoundException
or UnauthorizedAccessException
.
Unlike Java, C# does not force the programmer to manually handle the exceptions.
It is the programmer's decision whether to manually handle an exception.
If the exception is not manually handled in the try/catch/finally
contruct, the exception is handled by CLR.
Releasing resources
I/O resources must be released. The reources can be released manually in the
finally clause with the Dispose
method. The using
keyword can be used for automatic release of the resources. Also, the
methods in the File
class release the resources for us.
Example text file
In the examples, we use this simple text file:
The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
C# File.ReadAllText
File
provides static methods for the creation, copying, deletion,
moving, and opening of a single file, and aids in the creation of FileStream
objects.
File.ReadAllText
is not suited for
reading very large files.
The File.ReadAllText
opens a file, reads all text in the file
with the specified encoding, and then closes the file.
using System.Text; var path = "/home/janbodnar/Documents/thermopylae.txt"; var text = File.ReadAllText(path, Encoding.UTF8); Console.WriteLine(text);
The program reads the contents of the thermopylae.txt
file
and prints them to the console.
var text = File.ReadAllText(path, Encoding.UTF8);
We read the whole file into a string in one shot. In the second argument, we specify the encoding.
$ dotnet run The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
C# File.ReadAllLines
File.ReadAllLines
opens a text file, reads all lines of the file
into a string array, and then closes the file.
File.ReadAllLines
is a convenient method for reading files in C#.
It should not be used when dealing with very large files.
var path = "/home/janbodnar/Documents/thermopylae.txt"; string[] lines = File.ReadAllLines(path); foreach (string line in lines) { Console.WriteLine(line); }
The example reads all lines from a file into a string array. We go through the array in a foreach loop and print each line to the console.
C# create file
The File.CreateText
creates or opens a file for writing
UTF-8 encoded text. If the file already exists, its contents are overwritten.
var path = "/home/janbodnar/Documents/cars.txt"; using var sw = File.CreateText(path); sw.WriteLine("Hummer"); sw.WriteLine("Skoda"); sw.WriteLine("BMW"); sw.WriteLine("Volkswagen"); sw.WriteLine("Volvo");
In the example, we create a cars.txt
file and write some car names
into it.
using var sw = File.CreateText(path);
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.
C# create, last write, last access time
With the File
class, we can get the creation, last write and last
access times of a file. The Exists
method determines whether the
specified file exists.
var path = "cars.txt"; if (File.Exists(path)) { Console.WriteLine(File.GetCreationTime(path)); Console.WriteLine(File.GetLastWriteTime(path)); Console.WriteLine(File.GetLastAccessTime(path)); }
If a specified file exists, we determine its creation, last write, and last access times.
if (File.Exists(path))
The Exists
method returns true
if the caller has the
required permissions and path contains the name of an existing file; otherwise,
false
. This method also returns false
if path is
null
, an invalid path, or a zero-length string.
Console.WriteLine(File.GetCreationTime(path)); Console.WriteLine(File.GetLastWriteTime(path)); Console.WriteLine(File.GetLastAccessTime(path));
We get creation time, last write time and last access time of the specified file.
$ dotnet run 2/25/2021 11:41:19 AM 2/25/2021 11:41:19 AM 2/25/2021 11:41:27 AM
C# copy file
The File.Copy
method copies an existing file to a new file. It
allows to overwrite a file of the same name.
var srcPath = "/home/janbodnar/Documents/cars.txt"; var destPath = "/home/janbodnar/Documents/cars2.txt"; File.Copy(srcPath, destPath, true); Console.WriteLine("File copied");
Then we copy the contents of the file to another file.
var srcPath = "/home/janbodnar/Documents/cars.txt"; var destPath = "/home/janbodnar/Documents/cars2.txt";
File.Copy(srcPath, destPath, true);
The Copy
method copies the file. The third parameter specifies
if the file should be overwritten, if it exists.
C# 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.
StreamReader? sr = null; var path = "thermopylae.txt"; try { sr = new StreamReader(path); 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 { sr?.Dispose(); }
In this example, we read characters from a file on a disk. We manually release allocated resources.
sr = new StreamReader(path); 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 { sr?.Dispose(); }
In the finally block, the Dispose
method cleans up the resources.
With the null-conditinal operator we call the method only if the variable is not
null
.
C# using statement
The using
statement defines a scope at the end of which an
object will be disposed. It provides a convenient syntax that ensures the
correct use of IDisposable
objects.
var path = "/home/janbodnar/Documents/thermopylae.txt"; using (var sr = new StreamReader(path)) { Console.WriteLine(sr.ReadToEnd()); }
The example reads the contents of the thermopylae.txt
file.
The resources are released with the using
statement. If we do not
handle the IO exceptions, they will be handled by CLR.
C# using declaration
The using declaration is a variable declaration preceded by the
using
keyword. It tells the compiler that the variable being
declared should be disposed at the end of the enclosing scope. The using
declaration is available since C# 8.0.
var path = "thermopylae.txt"; using var sr = new StreamReader(path); Console.WriteLine(sr.ReadToEnd());
The example reads the contents of the thermopylae.txt
file.
The resources are automatically cleaned when the sr
variable
goes out of scope (at the end of the Main
method.).
C# MemoryStream
MemoryStream
is a stream which works with data in a computer
memory.
using var 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);
We write six numbers to a memory with a MemoryStream
. Then we read
those numbers and print them to the console.
using var 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.
$ dotnet run 9 11 6 8 3 7
C# StreamReader
StreamReader
reads characters from a byte stream. It defaults to
UTF-8 encoding.
var path = "/home/janbodnar/Documents/thermopylae.txt"; using var sr = new StreamReader(path); while (sr.Peek() >= 0) { Console.WriteLine(sr.ReadLine()); }
We read the contents of a file. This time we read the file line by line using
the ReadLine
method.
while (sr.Peek() >= 0) { Console.WriteLine(sr.ReadLine()); }
The Peek
method returns the next available character but does not
consume it. It indicates whether we can call the ReadLine
method
again. It returns -1
if there are no characters to be read.
C# count lines
In the next example, we be counting lines.
int count = 0; var path = "/home/janbodnar/Documents/thermopylae.txt"; using var sr = new StreamReader(path); while (sr.ReadLine() != null) { count++; } Console.WriteLine($"There are {count} lines");
We use a StreamReader
and a while loop.
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.
$ dotnet run There are 3 lines
C# StreamWriter
StreamWriter
writes characters to a stream in a particular
encoding.
var path = "/home/janbodnar/Documents/newfile.txt"; using var sw = new StreamWriter(path); sw.WriteLine("Today is a beautiful day.");
The example writes a string to a file with StreamWriter
.
using var sw = new StreamWriter(path);
We create a new StreamWriter
. The default enconding is UTF-8. The
StreamWriter
takes a path as a parameter. If the file exists, it is
overwritten; otherwise, a new file is created.
C# FileStream
FileStream
provides a stream for a file, supporting both
synchronous and asynchronous read and write operations.
StreamReader
and StreamWriter
work with text data,
while FileStream
works with bytes.
using System.Text; var path = "/home/janbodnar/Documents/newfile2.txt"; using var fs = new FileStream(path, FileMode.Append); var text = "Фёдор Михайлович Достоевский\n"; byte[] bytes = new UTF8Encoding().GetBytes(text); fs.Write(bytes, 0, bytes.Length);
We write some text in Russian Cyrillic to a file.
using System.Text;
The UTF8Encoding
class is located in the System.Text
namespace.
using var fs = new FileStream(path, 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.
var text = "Фёдор Михайлович Достоевский";
This is text in Russian Cyrillic.
byte[] bytes = new UTF8Encoding().GetBytes(text);
An array of bytes is created from the text in Russian Cyrillic.
fs.Write(bytes, 0, bytes.Length);
We write the bytes to the file stream.
$ cat /home/janbodnar/Documents/newfile2.txt Фёдор Михайлович Достоевский
We show the contents of the created file.
C# 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.Xml; string path = "/home/janbodnar/Documents/languages.xml"; using var xreader = new XmlTextReader(path); xreader.MoveToContent(); while (xreader.Read()) { var node = xreader.NodeType switch { XmlNodeType.Element => String.Format("{0}: ", xreader.Name), XmlNodeType.Text => String.Format("{0} \n", xreader.Value), _ => "" }; Console.Write(node); }
This example reads data from the XML file and prints it to the terminal.
using System.Xml;
The System.Xml
namespace contains
classes related to Xml reading and writing.
using var xreader = new XmlTextReader(path);
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.
var node = xreader.NodeType switch { XmlNodeType.Element => String.Format("{0}: ", xreader.Name), XmlNodeType.Text => String.Format("{0} \n", xreader.Value), _ => "" }; Console.Write(node);
Here we print the element name and the element text.
$ dotnet run language: Python language: Ruby language: Javascript language: C#
C# create, move directory
The System.IO.Directory
is a class that has static methods for
creating, moving, and enumerating through directories and subdirectories.
Directory.CreateDirectory("temp"); Directory.CreateDirectory("newdir"); Directory.Move("temp", "temporary");
We create two directories and rename one of the created ones. The directories are created in the project folder.
Directory.CreateDirectory("temp");
The CreateDirectory
method creates a new directory.
Directory.Move("temp", "temporary");
The Move
method gives a specified directory a new name.
C# DirectoryInfo
DirectoryInfo
exposes instance methods for creating, moving,
and enumerating through directories and subdirectories.
var path = "/home/janbodnar/Documents"; var dirInfo = new DirectoryInfo(path); string[] files = Directory.GetFiles(path); DirectoryInfo[] dirs = dirInfo.GetDirectories(); foreach (DirectoryInfo subDir in dirs) { Console.WriteLine(subDir.Name); } foreach (string fileName in files) { Console.WriteLine(fileName); }
We use the DirectoryInfo
class to traverse a specific directory and
print its contents.
var path = "/home/janbodnar/Documents"; var DirInfo = new DirectoryInfo(path);
We show the contents of the specified directory.
string[] files = Directory.GetFiles(path);
We get all files of the directory using the static
GetFiles
method.
DirectoryInfo[] dirs = dir.GetDirectories();
We get all the 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.
Source
In this article we have covered Input/Output operations in C#.
Author
List all C# tutorials.