BuildQuill
C# learning path
22 of 25
Lesson 22 of 25
Lesson 22Beginner20 min

C# Files and Directories

Persist program data, build paths safely, choose whole-file or streaming APIs, and handle external storage as an unreliable boundary.

Before this lesson

  • Strings
  • Exceptions and using
  • Collections

Read and write small text files

Build paths without manual separators

Choose convenience APIs or streams from file size and control needs

The short answer

System.IO provides File, Directory, Path, and stream APIs. Use File convenience methods for small whole-file operations, streams for incremental control or large data, Path.Combine for paths, and explicit exception handling at the application boundary.

01

Memory disappears; files persist

Variables and collections hold program state while the process runs. When it ends, that in-memory state is gone unless the program writes it somewhere durable. A text file is the simplest persistence boundary and helps reveal issues that do not exist with hardcoded data: paths, encoding, permissions, partial writes, and malformed content.

A file is bytes with a name in a filesystem. Text APIs encode characters into bytes and decode them back. Modern .NET convenience methods use UTF-8 defaults appropriate for much text, but interoperability may require an explicit encoding agreed with another system.

02

Start with whole-file convenience methods

File.WriteAllText replaces a file with one string, AppendAllText adds text, WriteAllLines writes a sequence of lines, and matching Read methods retrieve content. These methods open, use, and close the file internally, making them a good fit for small settings, exports, and learning projects.

Replacing and appending are different data policies. An activity log usually appends; a generated report may replace; a critical settings file may require writing a temporary file and atomically replacing the old one. Choose deliberately rather than using the first method autocomplete suggests.

C#
string path = "visits.txt";
File.AppendAllText(path, DateTime.UtcNow + Environment.NewLine);

string content = File.ReadAllText(path);
Console.WriteLine(content);
03

Build paths as paths

Hardcoding separators such as \ or / makes platform assumptions and creates doubled or missing separators. Path.Combine joins segments according to the current platform. AppContext.BaseDirectory locates the application base, while Environment.SpecialFolder APIs identify user locations when that is truly where data belongs.

Never combine untrusted text into a sensitive base path and assume it stays inside that directory. Inputs containing rooted paths or parent traversal segments can escape the intended location. Validate allowed file names, resolve the full path, and confirm it remains within the approved root for real upload or file-serving features.

C#
string folder = Path.Combine("data", "reports");
Directory.CreateDirectory(folder);

string reportPath = Path.Combine(folder, "weekly.txt");
File.WriteAllText(reportPath, "Completed: 7");
04

Use streams when the whole file should not be in memory

ReadAllText loads an entire file. For a large log, streaming one line at a time uses bounded memory and lets processing begin before the end is read. StreamReader and StreamWriter expose that control and must be disposed with using.

File APIs face external change. A file can disappear after File.Exists returns true, so existence checks do not replace handling the operation's exceptions. Catch specific failures where the program can offer recovery, and do not reveal internal server paths to untrusted users.

NeedPreferTradeoff
Small complete textReadAllText / WriteAllTextSimple; loads all content
Small line collectionReadAllLines / WriteAllLinesConvenient array; loads all lines
Large or incremental textStreamReader / StreamWriterMore control and cleanup
Structured application dataSerialization plus schema/modelAdds format and compatibility decisions
C#
using (StreamReader reader = File.OpenText("events.txt"))
{
  string line;
  while ((line = reader.ReadLine()) != null)
  {
      Console.WriteLine(line);
  }
}

Good habits

  • Use Path.Combine instead of manual separators.
  • Keep file I/O outside calculation methods when possible.
  • Validate untrusted names and contain resolved paths inside an allowed root.
  • Decide overwrite, append, backup, and encoding policies explicitly.

Quick knowledge check

Answer before you reveal.

01Does File.Exists guarantee the later read will succeed?

No. The file or permissions can change between operations. Handle failures from the actual read.

02When is ReadAllText a poor choice?

When a file may be too large to load comfortably or processing should be incremental.

Practice challenge

Now build it without copying.

Create a journal program that appends a timestamped line, reads all entries, and prints how many entries exist. Use a dedicated data directory and Path.Combine.

You are done when

  • Running twice preserves the first entry
  • The directory is created if absent
  • Path separators are not hardcoded
  • Missing or inaccessible storage produces a useful boundary message

Stretch: Stream the entries and print only lines containing a search term without loading the entire file.

Open challenge in playground

Lesson checkpoint

One small step locks it in

Mark this lesson complete, then keep the momentum going.

Complete and continue

Clear up the details

Frequently asked questions

Where should an application save files?

It depends on the application, platform, deployment, permissions, and whether data belongs to a user or service. Avoid assuming the current working directory is permanent writable storage.

Should I store complex data as CSV or JSON?

Choose a format based on structure and consumers. CSV fits tabular data with careful escaping; JSON fits nested structured data. Use established parsers and serializers rather than hand-building either format.

What is a stream?

It is an abstraction for reading or writing a sequence of bytes over time, whether the source is a file, memory, network connection, or another provider.