It’s time to learn how to work with files in C#. For that purpose, in this article, we will cover two classes StreamWriter and StreamReader.
For the complete navigation of this series check out: C# Back to Basics.
If you want to download the source code for our examples, you can do that from here StreamWriter and StreamReader C# Source Code.
The StreamReader and StreamWriter classes enable the reading and writing actions to a file. Both of these classes exist in the System.IO
namespace as well as many other classes for working with files and directories.
We are going to cover the following sections:
- Creating Objects for StreamWriter and StreamReader
- StreamReader Methods
- StreamWriter Methods
- Using Block
- Conclusion
Creating Objects for StreamWriter and StreamReader
To create objects for the StreamReader
and StreamWriter
classes we need to use the standard initialization for the reference data types. We can execute this initialization in a couple of ways but the most common is by only providing an address to the file:
StreamReader readerRelativePath = new StreamReader("test.txt"); StreamReader readerAbsolutePath = new StreamReader("C:\\MyProject\\test.txt"); StreamWriter writerRelativePath = new StreamWriter("test.txt"); StreamWriter writerAbsolutePath = new StreamWriter("C:\\MyProject\\test.txt");
As we can see from the code above, we can provide the relative or absolute path to our file. If we provide a relative path (just a name and extension) Visual Studio will place a file inside the projectName/bin/debug folder.
StreamReader Methods
StreamReader contains many different methods to work with files but we are going to mention few of those.
The Read()
method will return next sign as an integer number or -1 if we reached the end of the file. We can use explicit conversion (cast) to convert that integer into a char
type:
static void Main(string[] args) { StreamReader sr = new StreamReader("test.txt"); int x; char ch; x = sr.Read(); while(x != -1) { ch = (char)x; //do stuff here x = sr.Read(); } }
The ReadLine()
method will return a whole line as a string. If we reached the end of the file it will return null:
static void Main(string[] args) { StreamReader sr = new StreamReader("test.txt"); string line = sr.ReadLine(); while(line != null) { //some coding line = sr.ReadLine(); } }
The ReadToEnd()
method returns a whole file in one string. If there is nothing more to read it will return an empty string.
The Peek()
method checks the next character in the file or if it finds nothing it will return -1:
static void Main(string[] args) { StreamReader sr = new StreamReader("test.txt"); string line; while(sr.Peek() != -1) { line = sr.ReadLine(); //some coding } }
StreamWriter Methods
The two most important methods for the StreamWriter class is the Write()
and WriteLine()
. With the Write()
method we write a line inside a file but without moving to another line after. But with the WriteLine()
method we write a line inside a file and moving to another line.
It is very important to call the Close()
method, after we are finished with using reader or writer objects.
Example1: Create an application that writes five random numbers from 1 to 100 to a file named numbers.txt. Then it will read all the numbers from that file, print them out and print the maximum number:
class Program { public static void WriteToFile(string path) { StreamWriter sw = new StreamWriter(path); Random r = new Random(); //class to generate random numbers for(int i = 1; i <= 5; i++) { sw.WriteLine(r.Next(1,101)); } sw.Close(); } public static void PrintNumbersAndMax(string path) { StreamReader sr = new StreamReader(path); string line = sr.ReadLine(); Console.WriteLine(line); int max = Convert.ToInt32(line); while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); int temp = Convert.ToInt32(line); if(temp > max) { max = temp; } } sr.Close(); Console.WriteLine($"Max number is: {max}"); } static void Main(string[] args) { WriteToFile("numbers.txt"); PrintNumbersAndMax("numbers.txt"); Console.ReadLine(); } }
As we can see, we have to use the Close
method to close our reader and writer. But there is an even better way to do this. By using the using
block.
Using Block
The using
block helps to manage our resources. It specifies a scope in which we use our resource, and once we leave that scope, the resource is going to be managed.
To use the using
block we need to specify the using
keyword, create resources inside parentheses and declare the scope of the using
block with the curly brackets:
using (Resource creation) { }
So, we can rewrite one of our methods form the previous example:
public static void PrintNumbersAndMax(string path) { using (StreamReader sr = new StreamReader(path)) { string line = sr.ReadLine(); Console.WriteLine(line); int max = Convert.ToInt32(line); while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); int temp = Convert.ToInt32(line); if (temp > max) { max = temp; } } Console.WriteLine($"Max number is: {max}"); } }
In this example, we are not using the Close
method because as soon as execution leaves the body of the using
statement, the StreamReader
object is going to be managed.
Conclusion
There we go. Right now, we have a good knowledge to manipulate files from C#.
In the next article, we are going to learn how to use File and Directory classes to manipulate files in C#.