Topic O - File I/O

Overview

All of the File I/O examples follow the same design pattern. This pattern is a form of what is called the Adapter Pattern. Each class has the following characteristics.

All of the text files used in this topic have their records delimited by carriage returns and their fields delimited by commas. File input is achieved through use of the Scanner class. The Scanner is also used to parse each record by specifying the use of the comma delimiter.

File Input/Output (IO) is all about using files for storing and retrieving data. These files exist “outside” of your program, and code is required to transfer the data between the file and the objects/variables in your program.

Data inside of files can be stored in either a binary or a text format. All of these examples will deal with text files. When using text files, the text itself needs to follow some pattern in order to distinguish the individual pieces of data from each other.

An old and traditional way of storing the text is to use a CSV (Comma-Separated Values) format. In this format, each row of information (also known as a “record” of data) is broken down into individual pieces of data by separating each piece (or “field”) by a comma. Rows are separated by a new line (or “carriage return”) marker. This format has had the advantage of being easy for humans to read and edit directly; it is also fairly flexible and programmers can easily adapt the form to fit the shape of the data they wish to store.

A more modern approach is to store the textual information in XML (eXtensible Markup Language) format. While also being human-readable and easy to format, XML has the advantage of making it easier for computer programs to read and save data that is both relational and complex. XML format uses “tags” (or “mark-up”) to wrap data. For example, if information on a person (“Bob Smith”) is stored in an XML file, it might look like this.

1 <Person>
2   <FirstName>Bob</FirstName>
3   <LastName>Smith</LastName>
4 </Person>

File Input/Output is best performed by having one class (or object) deal with formatting the data and a separate class to deal with directly accessing the file. In the following examples, the CSVFileIO and XMLFileIO classes read and write directly to the files, while the other classes (which end with “Adapter”) are concerned with determining the format of the data.

CSVFileIO

 1 /// <summary>
 2 /// The CSVFileStorage class provides simple reading of all the lines of text of\
 3  a CSV (Comma-Separated-Values) file.
 4 /// </summary>
 5 public class CSVFileIO
 6 {
 7     public string FilePath { get; private set; }
 8 
 9     /// <summary>
10     /// Initializes a new instance of the CSVFileStorage class.
11     /// </summary>
12     public CSVFileIO(string filePath)
13     {
14         FilePath = filePath;
15     }
16 
17     public List<string> ReadAllLines()
18     {
19         List<string> lines = new List<string>();
20         using (TextReader reader = new StreamReader(FilePath))
21         {
22             string aSingleLine = reader.ReadLine();
23             while (aSingleLine != null)
24             {
25                 lines.Add(aSingleLine);
26                 aSingleLine = reader.ReadLine();
27             }
28         }
29         return lines;
30     }
31 
32     public void WriteAllLines(List<string> lines, bool append)
33     {
34         using (TextWriter writer = new StreamWriter(FilePath, append))
35         {
36             foreach (string line in lines)
37             {
38                 writer.WriteLine(line);
39             }
40         }
41     }
42 }

XMLFileIO

 1 /// <summary>
 2 /// The XMLFileIO class manages loading and saving any type to an XML file.
 3 /// </summary>
 4 public class XMLFileIO<T>
 5 {
 6     public string FilePath { get; private set; }
 7 
 8     public XMLFileIO(string filePath)
 9     {
10         FilePath = filePath;
11     }
12 
13     public List<T> LoadAll()
14     {
15         XmlSerializer deserializer = new XmlSerializer(typeof(List<T>));
16         List<T> data;
17         using (TextReader textReader = new StreamReader(FilePath))
18         {
19             data = (List<T>)deserializer.Deserialize(textReader);
20             textReader.Close();
21         }
22         return data;
23     }
24 
25     public void SaveAll(List<T> data, bool append)
26     {
27         if (append && File.Exists(FilePath))
28         {
29             List<T> existingData = LoadAll();
30             existingData.AddRange(data);
31             data = existingData;
32         }
33         using (Stream stream = File.Open(FilePath, FileMode.OpenOrCreate))
34         {
35             XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
36             using (TextWriter writer = new StreamWriter(stream))
37             {
38                 serializer.Serialize(writer, data);
39                 writer.Close();
40             }
41         }
42     }
43 }

LOGs

  • A constructor that takes a valid File object
  • A method called fill() that takes an array as the parameter and returns the number of entries created in the array from having read the file.

Code Samples

  • PhoneNumberFileAdapter - This reads a text file to fill a list of PhoneNumber data.
  • StudentFileAdapter - This reads a text file to fill a list of Student data.
  • BookFileAdapter - This reads a text file to fill a list of Book data. Note that in this example, not every field in the record structure is used in creating the required Book objects; unused fields are simply read and “discarded” by the BookFileAdapter. In addition, the Book class and text file used in this example allows for a book to have multiple authors.
  • SafetyDepositBoxFileAdapter - This reads a text file to fill a list of SafetyDepositBox data. Note that in this particular example, some records may not have an account number for the box number.

PhoneNumberFileAdapter

This reads a text file to fill a list of PhoneNumber data.

  • Record Structure:
    1. Telephone Number : String
    2. Last Name : String
    3. First Name : String
 1 public class PhoneNumberFileAdapter
 2 {
 3     #region Read from a file
 4     public static List<PhoneNumber> LoadList(string filePath, FileFormat format)
 5     {
 6         List<PhoneNumber> data;
 7         if (format == FileFormat.CSV)
 8             data = LoadList(new CSVFileIO(filePath));
 9         else
10             data = LoadList(new XMLFileIO<PhoneNumber>(filePath));
11         return data;
12     }
13 
14     private static List<PhoneNumber> LoadList(CSVFileIO reader)
15     {
16         List<PhoneNumber> data = new List<PhoneNumber>();
17         List<string> lines = reader.ReadAllLines();
18         foreach (string individualLine in lines)
19         {
20             // code specifics here..
21             string[] fields = individualLine.Split(',');
22             string firstName = fields[0], lastName = fields[1], number = fields[\
23 2];
24             data.Add(new PhoneNumber(firstName, lastName, number));
25         }
26         return data;
27     }
28 
29     private static List<PhoneNumber> LoadList(XMLFileIO<PhoneNumber> reader)
30     {
31         return reader.LoadAll();
32     }
33     #endregion
34 
35     #region Write to a file
36     public static void SaveList(List<PhoneNumber> data, string fileName, FileFor\
37 mat format, bool append)
38     {
39         if (format == FileFormat.CSV)
40             SaveList(new CSVFileIO(fileName), data, append);
41         else
42             SaveList(new XMLFileIO<PhoneNumber>(fileName), data, append);
43     }
44 
45     private static void SaveList(CSVFileIO writer, List<PhoneNumber> data, bool \
46 append)
47     {
48         List<string> lines = new List<string>();
49         foreach (PhoneNumber item in data)
50         {
51             lines.Add(item.FirstName + "," + item.LastName + "," + item.Number);
52         }
53         writer.WriteAllLines(lines, append);
54     }
55 
56     private static void SaveList(XMLFileIO<PhoneNumber> writer, List<PhoneNumber\
57 > data, bool append)
58     {
59         writer.SaveAll(data, append);
60     }
61     #endregion
62 }

StudentFileAdapter

This reads a text file to fill a list of Student data.

  • Record Structure:
    1. Student Id : Integer
    2. Name : String
    3. Gender : GenderType
 1 public class StudentFileAdapter
 2 {
 3     #region Read from a file
 4     public static List<Student> LoadList(string filePath, FileFormat format)
 5     {
 6         List<Student> data;
 7         if (format == FileFormat.CSV)
 8             data = LoadList(new CSVFileIO(filePath));
 9         else
10             data = LoadList(new XMLFileIO<Student>(filePath));
11         return data;
12     }
13 
14     private static List<Student> LoadList(CSVFileIO reader)
15     {
16         List<Student> data = new List<Student>();
17         List<string> lines = reader.ReadAllLines();
18         foreach (string individualLine in lines)
19         {
20             // code specifics here..
21             string[] fields = individualLine.Split(',');
22             int id = System.Convert.ToInt32(fields[0]);
23             string name = fields[1];
24             GenderType gender = (GenderType)System.Enum.Parse(typeof(GenderType)\
25 , fields[2]);
26             data.Add(new Student(name, gender, id));
27         }
28         return data;
29     }
30 
31     private static List<Student> LoadList(XMLFileIO<Student> reader)
32     {
33         return reader.LoadAll();
34     }
35     #endregion
36 
37     #region Write to a file
38     public static void SaveList(List<Student> data, string fileName, FileFormat \
39 format, bool append)
40     {
41         if (format == FileFormat.CSV)
42             SaveList(new CSVFileIO(fileName), data, append);
43         else
44             SaveList(new XMLFileIO<Student>(fileName), data, append);
45     }
46 
47     private static void SaveList(CSVFileIO writer, List<Student> data, bool appe\
48 nd)
49     {
50         List<string> lines = new List<string>();
51         foreach (Student item in data)
52         {
53             lines.Add(item.StudentId.ToString() + "," + item.Name + "," + item.G\
54 ender.ToString());
55         }
56         writer.WriteAllLines(lines, append);
57     }
58 
59     private static void SaveList(XMLFileIO<Student> writer, List<Student> data, \
60 bool append)
61     {
62         writer.SaveAll(data, append);
63     }
64     #endregion
65 }

BookFileAdapter

This reads a CSV text file to fill a list of Book data. Note that in this example, not every field in the record structure of CSV files is used in creating the required Book objects; unused fields are simply read and “discarded” by the BookFileAdapter. In addition, the Book class and text file used in this example allows for a book to have multiple authors.

  • Record Structure in the file:
    1. Title : String
    2. Authors : String - delimited by semi-colons
    3. Publication year : Integer
    4. Publisher : String
    5. Format : String
    6. Dewey Decimal Number : String - could be empty in the file
    7. ISBN : String
    8. Number of Pages : Integer
    9. Cover Price : Money
  1 /// <summary>
  2 /// BookFileAdapter provides a way to read and write
  3 /// Book information to and from text files.
  4 /// </summary>
  5 /// <remarks>
  6 /// This class requires that the file structure be in the following format:
  7 /// <ol><li>Title : String
  8 /// <li>Authors : String[]
  9 /// <ol><li>The Author information is a semicolon (;) separated list of authors
 10 /// </li></ol>
 11 /// <li>Publication Year : Integer
 12 /// <li>Publisher : String
 13 /// <li>Format : String
 14 /// <li>Dewey Decimal Number : String
 15 /// <li>ISBN : String
 16 /// <li>Number of Pages : Integer
 17 /// <li>Cover Price : Money
 18 /// </ol>
 19 /// <para>
 20 /// This method fills a List of Book data. Each Book only needs
 21 /// the title, the authors (as an array of Strings), and the ISBN. 
 22 /// Therefore, the other data in the file will simply be discarded as it
 23 /// is read. In the file, the Author information is a semicolon (;)
 24 /// separated list of authors.
 25 /// </para>
 26 /// </remarks>
 27 public class BookFileAdapter
 28 {
 29     #region Read from a file
 30     public static List<Book> LoadList(string filePath, FileFormat format)
 31     {
 32         List<Book> data;
 33         if (format == FileFormat.CSV)
 34             data = LoadList(new CSVFileIO(filePath));
 35         else
 36             data = LoadList(new XMLFileIO<Book>(filePath));
 37         return data;
 38     }
 39 
 40     private static List<Book> LoadList(CSVFileIO reader)
 41     {
 42         List<Book> data = new List<Book>();
 43         List<string> lines = reader.ReadAllLines();
 44         foreach (string individualLine in lines)
 45         {
 46             string title, authorList;
 47             ISBN bookUPC;
 48             // Parse the record
 49             string[] fields = individualLine.Split(',');
 50             title = fields[0];
 51             authorList = fields[1];
 52             // fields[2] Publication Year
 53             // fields[3] Publisher
 54             // fields[4] Format
 55             // fields[5] Dewey Decimal Number
 56             bookUPC = new ISBN(fields[6]);
 57             // fields[7] Number of Pages
 58             // fields[8] Cover Price
 59 
 60             // Parse the authorList
 61             string[] authors = null;
 62             authors = authorList.Split(';');
 63 
 64             // Create the book and add it to the array
 65             data.Add(new Book(title, authors, bookUPC));
 66         }
 67         return data;
 68     }
 69 
 70     private static List<Book> LoadList(XMLFileIO<Book> reader)
 71     {
 72         return reader.LoadAll();
 73     }
 74     #endregion
 75 
 76     #region Write to a file
 77     public static void SaveList(List<Book> data, string fileName, FileFormat for\
 78 mat, bool append)
 79     {
 80         if (format == FileFormat.CSV)
 81             SaveList(new CSVFileIO(fileName), data, append);
 82         else
 83             SaveList(new XMLFileIO<Book>(fileName), data, append);
 84     }
 85 
 86     private static void SaveList(CSVFileIO writer, List<Book> data, bool append)
 87     {
 88         List<string> lines = new List<string>();
 89         foreach (Book item in data)
 90         {
 91             string formattedRecord;
 92             formattedRecord = item.Title;
 93             formattedRecord += string.Join(";", item.Authors);
 94             formattedRecord += item.Isbn.BarCode;
 95 
 96             lines.Add(formattedRecord);
 97         }
 98         writer.WriteAllLines(lines, append);
 99     }
100 
101     private static void SaveList(XMLFileIO<Book> writer, List<Book> data, bool a\
102 ppend)
103     {
104         writer.SaveAll(data, append);
105     }
106     #endregion
107 }

SafetyDepositBoxFileAdapter

This reads a text file to fill a list of SafetyDepositBox data. Note that in this particular example, some records may not have an account number for the box number.

  • Record Structure:
    1. Box Number : Integer
    2. Account Number : Integer - may not exist
 1 /// <summary>
 2 /// SafetyDepositBoxFileAdapter provides a way to read and write
 3 /// SafetyDepositBox information to and from text files.
 4 /// </summary>
 5 /// <remarks>
 6 /// This class requires that the file structure be in the following format:
 7 /// <ol><li>StudentId : Integer
 8 /// <li>StudentName : String
 9 /// <li>StudentGender : GenderType
10 /// </ol>
11 /// </remarks>
12 public class SafetyDepositBoxFileAdapter
13 {
14     #region Read from a file
15     public static List<SafetyDepositBox> LoadList(string filePath, FileFormat fo\
16 rmat)
17     {
18         List<SafetyDepositBox> data;
19         if (format == FileFormat.CSV)
20             data = LoadList(new CSVFileIO(filePath));
21         else
22             data = LoadList(new XMLFileIO<SafetyDepositBox>(filePath));
23         return data;
24     }
25 
26     private static List<SafetyDepositBox> LoadList(CSVFileIO reader)
27     {
28         List<SafetyDepositBox> data = new List<SafetyDepositBox>();
29         List<string> lines = reader.ReadAllLines();
30         foreach (string individualLine in lines)
31         {
32             // code specifics here..
33             string[] fields = individualLine.Split(',');
34             int boxNumber, accountNumber;
35             boxNumber = Convert.ToInt32(fields[0]);
36             if (fields.Length > 1)
37             {
38                 accountNumber = Convert.ToInt32(fields[1]);
39                 data.Add(new SafetyDepositBox(boxNumber, accountNumber));
40             }
41             else
42             {
43                 data.Add(new SafetyDepositBox(boxNumber));
44             }
45         }
46         return data;
47     }
48 
49     private static List<SafetyDepositBox> LoadList(XMLFileIO<SafetyDepositBox> r\
50 eader)
51     {
52         return reader.LoadAll();
53     }
54     #endregion
55 
56     #region Write to a file
57     public static void SaveList(List<SafetyDepositBox> data, string fileName, Fi\
58 leFormat format, bool append)
59     {
60         if (format == FileFormat.CSV)
61             SaveList(new CSVFileIO(fileName), data, append);
62         else
63             SaveList(new XMLFileIO<SafetyDepositBox>(fileName), data, append);
64     }
65 
66     private static void SaveList(CSVFileIO writer, List<SafetyDepositBox> data, \
67 bool append)
68     {
69         List<string> lines = new List<string>();
70         foreach (SafetyDepositBox item in data)
71         {
72             string lineOfText = item.BoxNumber.ToString();
73             if (item.IsLeased)
74                 lineOfText += "," + item.AccountNumber;
75             lines.Add(lineOfText);
76         }
77         writer.WriteAllLines(lines, append);
78     }
79 
80     private static void SaveList(XMLFileIO<SafetyDepositBox> writer, List<Safety\
81 DepositBox> data, bool append)
82     {
83         writer.SaveAll(data, append);
84     }
85     #endregion
86 }

Practice Exercises

  1. PersonFileAdapter – This reads a text file to fill an array of Person data.
  2. SongFileAdapter – This reads a text file to fill an array of Song data.

PersonFileAdapter

This reads a text file to fill an array of Person data.

  • Record Structure:
    1. Record Structure:
    2. First Name : String
    3. Last Name : String
    4. Gender : GenderType
    5. Birthdate : Date

SongFileAdapter

This reads a text file to fill an array of Song data.

  • Record Structure:
    1. Record Structure:
    2. Title : String
    3. Song Writer : String
    4. Length : RunningTime