Welcome to Linking an Object Application to Simple Files!
Hello and welcome! In your CCEA AS Level journey with Event-Driven Programming, you have built fantastic forms with buttons, textboxes, and custom classes. But have you noticed that every time you close your program, all your data vanishes into thin air?
In this chapter, you will learn how to make your data persistent. This simply means saving data to a file on your disk so it is still there when you open the program tomorrow. We will explore how to take data from your C# objects, save it into simple text files, and read it back to recreate your objects.
Quick Encouragement: Don't worry if reading and writing files feels confusing at first. It follows a very predictable step-by-step recipe every single time!
1. The Big Picture: RAM vs Persistent Storage
When your program creates an object (like a new Student or Product), it lives in RAM (Random Access Memory). RAM is volatile, meaning everything disappears when the application closes or the computer loses power.
To keep data permanently, we must transfer it from RAM to secondary storage (such as a hard drive or SSD) in the form of a text file (often with a .txt or .csv extension).
Everyday Analogy: RAM is like writing notes on a whiteboard during a lesson—it's fast and easy to change, but it gets wiped clean at the end of the day. Saving to a file is like writing those notes neatly into your notebook to take home!
Key Takeaway:
Persistence means storing data outside the application memory so it can be reloaded and used across different sessions.
2. Understanding File Streams: StreamWriter and StreamReader
In C#, moving data between your program and a file is done using something called a stream. Think of a stream as a one-way pipeline carrying a flow of text data.
There are two essential classes you need to know in the System.IO namespace:
• StreamWriter: Writes characters and lines out from your program into a file (saving data).
• StreamReader: Reads characters and lines from an existing file into your program (loading data).
Did You Know?
Before you can use StreamReader or StreamWriter, you must include the line using System.IO; at the very top of your C# code file!
3. Writing Objects to a Text File
When you have a list of objects (for example, a list of Customer objects), you cannot just dump the raw object into a text file. You need to convert the object's properties into a formatted text string. The most common format is CSV (Comma-Separated Values).
The Standard Recipe for Writing:
1. Open or create the file using a StreamWriter.
2. Loop through your collection of objects.
3. Format each object's data into a single line (delimited by commas or hyphens).
4. Write the line using WriteLine().
5. Close the writer to save the changes.
Code Example: Saving a List of Products
// Imagine we have a Product class with ID, Name, and Price
StreamWriter sw = new StreamWriter("products.txt", false);
foreach (Product p in productList)
{
// Write: 101,Keyboard,29.99
sw.WriteLine(p.ID + "," + p.Name + "," + p.Price);
}
sw.Close();
Understanding Overwrite vs. Append Mode
Notice the second parameter in the StreamWriter constructor: new StreamWriter("filename.txt", booleanValue).
• false (Overwrite mode): Erases anything currently inside the file and starts fresh. Perfect when saving an entire updated list.
• true (Append mode): Keeps existing text and adds new data onto the very end of the file. Perfect for log files or adding single new records.
Key Takeaway:
Always close your StreamWriter with .Close(), or data might get trapped in the memory buffer and never actually write to the physical disk!
4. Reading a File and Rebuilding Objects
Loading data is the exact reverse of saving. We read each line from the file, break it apart into individual values, and use those values to instantiate brand-new objects.
The Standard Recipe for Reading:
1. Check if the file exists using File.Exists("filename.txt").
2. Open the file using a StreamReader.
3. Use a while loop to read line by line until the end of the file is reached.
4. Use the .Split(',') method to break the comma-separated line into an array of strings.
5. Convert data types (e.g., parsing strings into integers or doubles).
6. Create a new object and add it to your list.
7. Close the reader.
Code Example: Loading Products into a List
if (File.Exists("products.txt"))
{
StreamReader sr = new StreamReader("products.txt");
string line;
while (!sr.EndOfStream)
{
line = sr.ReadLine();
string[] parts = line.Split(',');
int id = int.Parse(parts[0]);
string name = parts[1];
double price = double.Parse(parts[2]);
Product newProd = new Product(id, name, price);
productList.Add(newProd);
}
sr.Close();
}
Memory Aid: The Reading Rhythm
Remember the 4-step loop rhythm: Read \(\rightarrow\) Split \(\rightarrow\) Parse \(\rightarrow\) Construct.
1. Read the raw line string.
2. Split by the delimiter character.
3. Parse each piece into the right data type.
4. Construct the new object and add it to your collection.
Key Takeaway:
Text files only store strings. You must explicitly convert numbers using int.Parse() or Convert.ToDouble() before setting your object properties.
5. Robust File Handling with Try-Catch Blocks
Working with files can be risky. What if the user deletes the file? What if the disk is full? What if the file is locked by another program? These situations cause Exceptions (runtime crashes).
In C#, we protect our file operations by wrapping them inside a try-catch-finally block.
Structure of a Safe File Operation:
• try block: Place your file reading or writing code here.
• catch block: Handles any error gracefully (e.g., displaying a friendly message box instead of crashing).
• finally block: Guaranteed to run no matter what happens. This is where we safely close the file stream.
Example: Safe File Reading
StreamReader sr = null;
try
{
sr = new StreamReader("scores.txt");
// Read file content here...
}
catch (FileNotFoundException)
{
MessageBox.Show("Error: The file could not be found.");
}
catch (Exception ex)
{
MessageBox.Show("An unexpected error occurred: " + ex.Message);
}
finally
{
if (sr != null)
{
sr.Close();
}
}
6. Common Mistakes to Avoid
• Forgetting to call .Close(): If you don't close a file, Windows will keep it locked, and other parts of your program won't be able to open it.
• Delimiter in user data: If you use a comma ',' to split fields, ensure your data (like an address) doesn't already contain a comma, or parts.Length will be wrong!
• Empty lines: An empty line at the end of a file will cause an IndexOutOfRangeException when you try to access parts[0]. Always check if the line has content before splitting.
• Forgetting using System.IO: If C# underlines StreamReader in red, make sure you imported the System.IO namespace at the top of your code file.
7. Quick Chapter Review
1. Namespace: Always include using System.IO;.
2. Saving: Use StreamWriter with sw.WriteLine() to output comma-delimited strings.
3. Overwrite vs Append: new StreamWriter("file.txt", false) replaces everything; new StreamWriter("file.txt", true) appends to the end.
4. Loading: Use StreamReader with while(!sr.EndOfStream), read the line, and split it with .Split(',').
5. Safety: Always use try-catch-finally and check File.Exists() to prevent application crashes.