Text Files
In this chapter
- Explain the need for persistent data
- Read and write data to a text file using the File class
- Define the full file path of a text file that is to be used
- Use the StreamWriter class to write one or more lines to a text file
- Use the StreamReader class to read a text file line by line
- Recognise that a struct is the implementation of a record in C#
- Know how to use a struct as a lightweight class
- Structure text file using CSV
While a program is running all data is stored in Random Access Memory (RAM), when the program ends (or the computer shuts down), all data in RAM will be lost. To make our data persist it needs to be saved to an external storage device, or network cloud, as a file.
There are two categories of classes in C# for dealing with Input/Output (I/O). There are classes that deal with (1) information about the file system itself e.g. copying or moving files and/or directories, and (2) reading and writing data from different sources. The different approaches to handling I/O can seem intimidating at first, particularly with the second category as the data can come from a variety of devices (e.g. peripherals, files, network connections). We've already been interacting with peripheral devices, reading data from the keyboard (Console.ReadLine()
) and writing data to the screen (Console.WriteLine
). In this chapter we'll expand into reading data from files, and writing data to files.
Before getting into the actual reading and writing of data there are some utility classes that deal with file-handling and directories.
From the syllabus
AQA: Fields, records and files (3.2.1.3/4.1.1.16): Be able to read/write from/to a text file.}
- A text file is a computer file that only contains text and has no special formatting such as bold text, italic text, images, etc.. A text file stores all of its data as characters represented by their Unicode code. Text files are read sequentially, from the beginning to the end.
Directories, Files and Paths
- Directory: methods for working with folders
- File: a class we can use for files
- Path: the route to the folder where a file is stored
Let's see, first, how these interact by checking to see if a file called settings.json
exists in a user's home directory. Here's the code:
There's a lot of code here but most is taken up with exception handling. Handling exceptions when dealing with files and directories is a must so we can deal with I/O errors correctly.
Firstly, we set the path:
Using the Combine
method of the Path
class we concatenate the name of our directory, "SAH", with the ApplicationData (%APPDATA%
) directory on our Windows box. In this case it is is set to C:\Users\<my username>\AppData\Roaming
. If we have the correct permissions, or know the file should default to a particular location, we could have hard-coded the path e.g. D:\\MyUSBDrive\\MyProjects
. NB. the double backslash is required, it's an escape sequence. If our program is saving data to a location on the user's file system we need to have permission to write to that directory. Alternatively, we could ask the user for the location.
We then use the Directory
class to check if this directory exists, if it does not exist it gets created with the CreateDirectory
method.
It's a similar procedure to check if the file, settings.json
, exists. If it does we can load the settings into our application (code not shown), or create the file (code not shown).
Warning
Before being able to save data to an external storage device the user will need the necessary permissions and access rights. Both Windows and Linux permit saving data to a user's personal directories. If you release applications "into the wild" make sure you know where data can be stored. It is probably safest to ask the user to select the directory.
The File
class can be used for simple and quick file access. To write (or read) from a file, the file has to be opened, and then closed when finished with. Here's a very simple example:
This looks very similar to writing text to the Console
, replacing the Console class with the identifier for our file. If the file does not exist it will be created and when we've finished with it the file is closed and made available for other programs to use.
This next example using the ReadAllText()
method to the contents of a file into a string variable:
This program assumes the path is the current working directory (though not recommended) and if the text file exists reads its contents to a string and displays the contents. Our program then asks the user for more content which is saved to the file (actually overwriting any existing content).
There is an equivalent method ReadAllLines()
which can read each line into an array of strings for further processing.
Other methods provided by the File
class include:
ReadAllText()
: the opposite toWriteAllText()
Create()
: Creates a file, returning aFileStream
Delete()
: Deletes a fileOpen()
: Opens a file for access with specified `FileMode`` i.e. read, write or appendClose()
: Closes a file. If a file has been opened, it must always be closed.Exists()
: Determines whether the file exists, returns a BooleanCopy()
: Copies an existing file to a new file specified in the second parameterMove()
: Moves a file to a new location, or file