The System.IO Namespace
File input/output in VB.NET is built on the same System.IO namespace that every other .NET language uses, giving you classes like File, Directory, FileStream, StreamReader, and StreamWriter. The File class exposes convenient static methods -- File.ReadAllText, File.WriteAllLines, File.Exists -- that handle opening, reading or writing, and closing a file in a single call, which is the right choice for small to medium files where you don't need fine-grained control over buffering. For larger files or streaming scenarios, you drop down to FileStream combined with a StreamReader or StreamWriter so data is processed incrementally instead of loaded entirely into memory.
Cricket analogy: Using File.ReadAllText is like a commentator reading the entire day's scorecard in one glance after play ends, while using StreamReader line by line is like a scorer updating the board ball by ball during a live Ashes Test, both are valid depending on whether you need the whole picture at once or incremental updates.
Reading and Writing Text Files
For most everyday file tasks, File.ReadAllText(path), File.ReadAllLines(path), and File.WriteAllText(path, content) are the simplest entry points because they open the file, perform the operation, and close the file handle automatically, eliminating the risk of a forgotten Close() call leaking a file handle. When appending rather than overwriting, File.AppendAllText or File.AppendAllLines adds content to the end of an existing file without truncating what's already there. VB.NET also exposes the My.Computer.FileSystem namespace as a higher-level, VB-specific convenience layer -- for example My.Computer.FileSystem.ReadAllText(path) -- which wraps System.IO calls but adds friendlier error messages and integrates with the My namespace's other helpers like My.Computer.FileSystem.CopyFile.
Cricket analogy: File.WriteAllText overwriting a file is like re-writing an entire scoresheet from scratch after a rain-delayed match is replayed, while AppendAllText is like adding overs to an existing scoresheet as a Test match continues into day two without erasing day one's record.
Imports System.IO
Module FileDemo
Sub Main()
Dim path As String = "C:\Data\notes.txt"
' Write (overwrites if the file exists)
File.WriteAllText(path, "Meeting notes for 2026-07-10" & Environment.NewLine)
' Append without truncating
File.AppendAllText(path, "Action item: follow up with client" & Environment.NewLine)
' Read back all lines
Dim lines() As String = File.ReadAllLines(path)
For Each line As String In lines
Console.WriteLine(line)
Next
' Using My.Computer.FileSystem for a VB-friendly copy
My.Computer.FileSystem.CopyFile(path, "C:\Data\notes_backup.txt", overwrite:=True)
End Sub
End ModuleStreams and the Using Statement
When you need control over encoding, buffering, or binary data, you work directly with FileStream, StreamReader, StreamWriter, or BinaryReader/BinaryWriter. Because these classes hold an open OS file handle, they implement IDisposable, and VB.NET's Using...End Using block guarantees the stream's Dispose() method runs even if an exception is thrown partway through, closing the underlying file handle deterministically rather than waiting for the garbage collector. Nesting multiple Using blocks (or using a single Using with multiple resources) is the standard pattern for wrapping a FileStream in a StreamReader, ensuring both layers are released in the correct order.
Cricket analogy: A Using block is like a stadium's strict gate-closing procedure after the last spectator leaves, guaranteeing the gates are locked even if an unexpected event, like a sudden rain stoppage, cuts the day short.
Prefer File.ReadAllText/WriteAllText for simple, one-shot text operations on small files. Reach for StreamReader/StreamWriter wrapped in Using blocks when you need to process large files line-by-line, control encoding explicitly, or keep memory usage low.
Handling File Errors and Directory Operations
File operations are inherently unreliable -- a file can be missing, locked by another process, or the path can be on a removed network drive -- so production VB.NET code wraps I/O in Try...Catch blocks targeting specific exceptions like FileNotFoundException, IOException (thrown when a file is locked), and UnauthorizedAccessException (thrown for permission issues), rather than a single generic Catch ex As Exception. Before attempting a read, checking File.Exists(path) avoids an unnecessary exception for the common case of a missing file, while Directory.CreateDirectory(path) safely creates a folder tree (it's a no-op if the directory already exists), which is the standard way to ensure an output directory exists before writing to it.
Cricket analogy: Catching specific exceptions like IOException versus a generic Exception is like a DRS review that specifically checks for a no-ball versus a vague 'something looked wrong' review, the specific check gives you actionable information the vague one doesn't.
Avoid catching the base Exception class around file I/O in production code -- it masks the real cause (missing file vs. locked file vs. permissions) and makes recovery logic impossible to write correctly. Catch the specific IOException-derived types you can meaningfully handle, and let unexpected exceptions propagate.
- System.IO's File and Directory classes provide simple static methods (ReadAllText, WriteAllLines, Exists) for common one-shot file operations.
- FileStream, StreamReader, and StreamWriter give fine-grained, memory-efficient control for large files or binary data.
- The Using...End Using block guarantees Dispose() is called on IDisposable stream objects, even when an exception occurs.
- My.Computer.FileSystem is a VB.NET-specific convenience layer over System.IO with friendlier helper methods like CopyFile.
- AppendAllText/AppendAllLines add content without truncating, while WriteAllText/WriteAllLines overwrite the file's existing contents.
- Catch specific exceptions (FileNotFoundException, IOException, UnauthorizedAccessException) instead of a generic Exception to enable meaningful error recovery.
- Directory.CreateDirectory safely ensures an output folder exists and is a no-op if it's already present.
Practice what you learned
1. Which method appends text to an existing file without erasing its current contents?
2. What guarantee does a VB.NET Using...End Using block provide for a FileStream object?
3. Why is catching a specific exception like IOException preferred over catching the base Exception class around file operations?
4. What happens if you call Directory.CreateDirectory on a path that already exists?
5. Which namespace provides VB.NET-specific convenience wrappers around System.IO, such as My.Computer.FileSystem.CopyFile?
Was this page helpful?
You May Also Like
Debugging VB.NET Applications
Practical techniques for diagnosing and fixing bugs in VB.NET applications using the Visual Studio debugger, breakpoints, and structured exception handling.
Unit Testing VB.NET Code
How to write and run automated unit tests for VB.NET code using MSTest, NUnit, or xUnit, including mocking and test structure best practices.
VB.NET and C# Interoperability
How VB.NET and C# code coexist and call into each other on the .NET CLR, and the practical rules for building mixed-language solutions.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics