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:
- Telephone Number : String
- Last Name : String
- 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:
- Student Id : Integer
- Name : String
- 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:
- Title : String
- Authors : String - delimited by semi-colons
- Publication year : Integer
- Publisher : String
- Format : String
- Dewey Decimal Number : String - could be empty in the file
- ISBN : String
- Number of Pages : Integer
- 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:
- Box Number : Integer
- 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
- PersonFileAdapter – This reads a text file to fill an array of Person data.
- 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:
- Record Structure:
- First Name : String
- Last Name : String
- Gender : GenderType
- Birthdate : Date
SongFileAdapter
This reads a text file to fill an array of Song data.
- Record Structure:
- Record Structure:
- Title : String
- Song Writer : String
- Length : RunningTime