Files play an extremely important role on a computer. They hold text, pictures, Microsoft Word documents, spreadsheets, and all sorts of other data. They also hold executable programs including programs that you write, programs provided by Microsoft and other software vendors, and even the programs that make up the operating system itself.
In this lesson you learn how to explore the filesystem. You also learn some basic techniques for reading and writing files. Using some fairly simple techniques, you can use text files to store and retrieve data used by a program.
Before you can manipulate a file, you need to be able to find it. This section describes .NET Framework classes that let you search the computer's filesystem.
The DriveInfo
class provides information about the system's drives. Its static GetDrives
function returns an array of DriveInfo
objects describing all of the system's drives. Table 29.1 summarizes the DriveInfo
class's most useful properties.
Property | Purpose |
AvailableFreeSpace |
The total number of bytes available. |
DriveFormat |
The drive format, as in NTFS or FAT32 . |
DriveType |
The drive type, as in Fixed or CDRom . |
IsReady |
Returns true if the drive is ready. A drive must be ready before you can use the AvailableFreeSpace , DriveFormat , TotalSize , or VolumeLabel properties. |
Name |
The drive's name, as in C: . |
RootDirectory |
A DirectoryInfo object representing the drive's root directory. |
TotalFreeSpace |
The number of bytes available, taking quotas into account. |
TotalSize |
The drive's total size in bytes. |
VolumeLabel |
The drive's label. |
The List Drives example program, which is in this lesson's code download and shown in Figure 29.1, uses DriveInfo
properties and methods to show information about the computer's drives. For details about how the program works, download it from the book's website
The DirectoryInfo
class provides information about directories. Table 29.2 summarizes useful DirectoryInfo
methods for manipulating directories.
Method | Purpose |
Create |
Creates a new directory. To use this, make a DirectoryInfo object, passing its constructor the name of the directory to create. Then call the Create method. |
CreateSubdirectory |
Creates a subdirectory inside this directory. |
Delete |
Deletes the directory. If you pass no parameters to this method, it deletes the directory only if it's empty. You can also pass it a boolean parameter indicating whether you want to delete all of the directory's files and subdirectories. |
GetDirectories |
Returns the directory's immediate subdirectories. Optionally you can include a search string to select particular subdirectories. |
GetFiles |
Returns the directory's files. Optionally you can include a search string to select particular files. |
MoveTo |
Moves the directory to a new path. |
The DirectoryInfo
class also provides a few useful properties, which are summarized in Table 29.3.
Property | Purpose |
Attributes |
The directory's attributes, such as Compressed , Hidden , or System . |
CreationTime |
The time at which the directory was created. |
Exists |
Returns true if the directory actually exists. |
FullName |
Gives the directory's fully qualified path. |
LastAccessTime |
The time at which the directory was last accessed. |
LastWriteTime |
The time at which the directory was last written. |
Name |
The directory's name without the path. |
Parent |
A DirectoryInfo object representing this directory's parent directory. |
Root |
The directory's filesystem root. |
Example program Use DirectoryInfo (found in this lesson's code download) uses a DirectoryInfo
object to display information about directories.
The Directory
class provides static methods for manipulating directories. Table 29.4 lists the most used methods. For simple tasks these are sometimes easier to use than the comparable DirectoryInfo
class methods because you don't need to create a DirectoryInfo
object to use them.
Method | Purpose |
CreateDirectory |
Creates the directory and any missing directories in its path up to the root. |
Delete |
Deletes a directory. |
Exists |
Returns true if the directory exists. |
GetCreationTime |
Returns the time at which the directory was created. |
GetDirectories |
Returns a directory's subdirectories. |
GetDirectoryRoot |
Returns the directory's root. |
GetFiles |
Returns a directory's files, optionally looking for files matching a pattern. |
GetLastAccessTime |
Returns the time at which a directory was last accessed. |
GetLastWriteTime |
Returns the time at which a directory was last written. |
GetParent |
Returns a DirectoryInfo object representing a directory's parent directory. |
Move |
Moves a file or directory to a new location. |
SetCreationTime |
Sets the directory's creation time. |
SetLastAccessTime |
Sets the directory's last access time. |
SetLastWriteTime |
Sets the directory's last write time. |
The FileInfo
class, as you can probably guess at this point, provides information about files. Table 29.5 summarizes useful FileInfo
methods for manipulating files.
Method | Purpose |
CopyTo |
Copies the file to a new location. |
Decrypt |
Decrypts a file that was encrypted by the Encrypt method. |
Delete |
Deletes the file. |
Encrypt |
Encrypts the file so it can only be read by the account used to encrypt it. |
MoveTo |
Moves the file to a new location. |
The FileInfo
class also provides some useful properties, summarized in Table 29.6.
Property | Purpose |
Attributes |
The file's attributes, such as Compressed , Hidden , or System . |
CreationTime |
The time at which the file was created. |
Directory |
A DirectoryInfo object for the directory containing the file. |
Exists |
Returns true if the file exists. |
Extension |
Returns the file's extension. |
FullName |
Gives the file's fully qualified path. |
IsReadOnly |
Returns true if the file is marked read-only. |
LastAccessTime |
The time at which the file was last accessed. |
LastWriteTime |
The time at which the file was last written. |
Length |
The file's size in bytes. |
Name |
The file's name without the path. |
Example program Use FileInfo (which is in this lesson's code download) uses a FileInfo
object to display information about files.
The File
class provides static methods for manipulating files (see Table 29.7). For simple tasks these are sometimes easier to use than the comparable FileInfo
class methods because you don't need to create a FileInfo
object to use them.
Method | Purpose |
AppendAllText |
Appends a string to the end of a file. |
Copy |
Copies a file to a new file. |
Create |
Creates a file. |
Decrypt |
Decrypts a file that was encrypted by the Encrypt method. |
Delete |
Deletes a file. |
Encrypt |
Encrypts the file so it can only be read by the account used to encrypt it. |
Exists |
Returns true if a file exists. |
GetAttributes |
Returns a file's attributes, such as ReadOnly , System , or Hidden . |
GetCreationTime |
Returns the time at which the file was created. |
GetLastAccessTime |
Returns the time at which a file was last accessed. |
GetLastWriteTime |
Returns the time at which a file was last written. |
Move |
Moves a file to a new location. |
ReadAllBytes |
Returns a file's contents in an array of bytes. |
ReadAllLines |
Returns the lines in a text file as an array of strings. |
ReadAllText |
Returns a text file's contents in a string. |
SetAttributes |
Sets a file's attributes. |
SetCreationTime |
Sets a file's creation time. |
SetLastAccessTime |
Sets a file's last access time. |
SetLastWriteTime |
Sets a file's last write time. |
WriteAllBytes |
Writes a file's contents from an array of bytes. |
WriteAllLines |
Writes a text file's contents from an array of strings. |
WriteAllText |
Writes a text file's contents from a string. |
The AppendAllText
, ReadAllLines
, ReadAllText
, WriteAllLines
, and WriteAllText
methods are particularly useful for reading and writing text files all at once, although you may still want to use the StreamReader
and StreamWriter
classes described later in this lesson if you need to manipulate files one line at a time.
The Path
class provides static methods that perform string operations on file paths. For example, you can use the ChangeExtension
method to change the extension part of a file's name. Table 29.8 summarizes the Path
class's most useful methods.
Method | Purpose |
ChangeExtension |
Changes a filename's extension. |
Combine |
Combines two path strings, adding a backslash between them if needed. |
GetDirectoryName |
Returns the directory name part of a path. |
GetExtension |
Returns the extension part of a filename. |
GetFileName |
Returns the filename part of a file's path. |
GetFileNameWithoutExtension |
Returns the filename part of a file's path without the extension. |
GetTempFileName |
Returns a name for a temporary file. |
GetTempPath |
Returns the path to the system's temporary folder. |
A computer can contain many kinds of files: web pages, video, audio, executable, and lots of others. At some level, however, files are all the same. They're just a series of bytes stored on a filesystem somewhere.
Thinking about files at this very low level lets you treat them uniformly. It lets you define common classes and methods that you can use to manipulate any kind of file.
Many programming languages, including C#, make working with files at a low level easier by defining the concept of a stream. A stream is simply an ordered series of bytes.
Stream objects provide methods for manipulating data at a low level. For example, the Stream
class provides Read
and Write
methods that move bytes of data between the stream and an array of bytes in your program.
Working with streams at this low level is convenient for some programs, but it makes day-to-day file handling difficult. You probably don't want to read the bytes from a text file and then reassemble them into characters.
The StreamReader
and StreamWriter
classes make reading and writing text streams much easier. As you can probably guess from their names, StreamReader
lets you read text from a stream and StreamWriter
lets you write text into a stream. If that stream happens to represent a file, then you're reading and writing files.
The StreamWriter
class provides several constructors to build a StreamWriter
associated with different kinds of streams. One of the simplest constructors takes a filename as a parameter. It opens the file for writing and associates the new StreamWriter
with it.
The following code shows how a program can open the file Memo.txt
for writing. If the file already exists, it is overwritten.
// Write into the file, overwriting it if it exists.
using (StreamWriter memoWriter = new StreamWriter("Memo.txt"))
{
// Write into the file.
...
}
Another version of the class's constructor takes a second bool
parameter that indicates whether you want to open the file for appending. If you set this parameter to true
, the StreamWriter
opens the existing file and prepares to add text to the end. If the file doesn't exit, the object silently creates a new file and gets ready to append.
The StreamWriter
class provides a Write
method to add text to the file. The WriteLine
method adds text followed by a new line. Both Write
and WriteLine
have overloaded versions that write various data types into the file: bool
, char
, string
, int
, decimal
, and so on. They also provide versions that take a format string and parameters much as the string.Format
method does.
The StreamWriter
provides one other very important method that I want to cover here: Close
. The Close
method closes the StreamWriter
and its associated file. When you use the Write
and WriteLine
methods, the StreamWriter
may actually buffer its output in memory and only write to the file when it has enough data stored up. The Close
method forces the StreamWriter
to flush its buffer into the file, and until you call Close
the data may not actually be in the file. If your program crashes or ends without calling Close
, there's a very good chance that some or all of your text will be lost.
The following code shows how a program could save the contents of a TextBox
in a file:
// Write the file, overwriting it if it exists.
using (StreamWriter memoWriter = new StreamWriter("Memo.txt"))
{
// Write the file.
memoWriter.Write(memoTextBox.Text);
memoWriter.Close();
}
The StreamReader
class lets you easily read text from a file. Like the StreamWriter
class, StreamReader
provides a constructor that takes a parameter giving the name of the file to open.
The StreamReader
constructor throws an exception if the file doesn't exist, so your program should verify that the file is there before you try to open it. For example, you can use the File
class's static Exists
method to see if the file exists.
The StreamReader
class provides a Read
method that lets you read from the file one or more bytes at a time, but usually you'll want to use its ReadLine
and ReadToEnd
methods.
As you may be able to guess, ReadLine
reads the next line from the file and returns it as a string. ReadToEnd
reads the rest of the file from the current position onward and returns it as a string.
The following code reads the file Memo.txt
and displays its contents in a TextBox
:
// Read the file.
using (StreamReader memoReader = new StreamReader("Memo.txt"))
{
memoTextBox.Text = memoReader.ReadToEnd();
memoReader.Close();
}
The StreamReader
's EndOfStream
property returns true
if the reader is at the end of the stream. This is particularly useful when you're reading a stream of unknown length. For example, the program can enter a while
loop that uses ReadLine
to read lines and continue as long as EndOfStream
is false
.
In this Try It, you build the program shown in Figure 29.2 to let the user search for files that match a pattern and that contain a target string. Enter a directory at which to start the search, select or enter a file pattern in the Pattern combo box, and enter a target string in the Search For textbox. When you click Search, the program searches for files matching the pattern and containing the target string.
In this lesson, you:
Load
event handler that places the application's startup path in the Directory textbox (just to have somewhere to start).Click
event handler that searches for the desired files.DirectoryInfo
class's GetFiles
method to search for files matching the pattern.FileInfo
class's ReadAllText
method to get the file's contents. Then use string methods to see if the text contains the target string.Load
event handler that places the application's startup path in the Directory textbox (just to have somewhere to start).
Use code similar to the following:
// Start at the startup directory.
private void Form1_Load(object sender, EventArgs e)
{
directoryTextBox.Text = Application.StartupPath;
}
Click
event handler that searches for the desired files.
Use code similar to the following:
// Search for files matching the pattern
// and containing the target string.
private void searchButton_Click(object sender, EventArgs e)
{
// Get the file pattern and target string.
string pattern = patternComboBox.Text;
string target = targetTextBox.Text.ToLower();
// Clear the result list.
fileListBox.Items.Clear();
// Search for files.
DirectoryInfo dirinfo =
new DirectoryInfo(directoryTextBox.Text);
foreach (FileInfo fileinfo in
dirinfo.GetFiles(pattern, SearchOption.AllDirectories))
{
// See if we need to look for target text.
if (target.Length > 0)
{
// If this file contains the target string,
// add it to the list.
string content =
File.ReadAllText(fileinfo.FullName).ToLower();
if (content.Contains(target))
fileListBox.Items.Add(fileinfo);
}
else
{
// Just add this file to the list.
fileListBox.Items.Add(fileinfo);
}
}
}
Array.Sort
to do the actual sorting.) Test the program on the file Names.txt
included in this lesson's download.Names.txt
included in this lesson's download.Label
s and TextBox
es for first name, last name, street, city, state, and ZIP code. When the form closes, save the values in the TextBox
es in a text file. When the program loads, reload the values. (Hint: Write each TextBox
's value on a separate line in the text file.)TextBox
. (This is so easy I wouldn't even bother using it as an exercise except it's actually useful. You can use it to record notes during the day and easily read them the next day.)NumericUpDown
control and then generates a text file containing a multiplication table that goes up to that number times itself. Use formatting to make the numbers line up in columns.TextBox
, a ListBox
, an Add button, and a Save button. When the user enters a value in the TextBox
and clicks Add, add the value to the ListBox
. When the user clicks Save, write the values from the ListBox
into a file and then clear the ListBox
. When the form loads, make it read the values back into the ListBox
.TextBox
and a File menu with Open, New, and Save As commands. Use an OpenFileDialog
and a SaveFileDialog
to let the user select the file to open and save. (Don't worry about any of the other things a real editor would need to handle, such as locked files and ensuring that the user doesn't close the program with unsaved changes.)