2CPP17 - File IO


Published on

This is an intermediate conversion course for C++, suitable for second year computing students who may have learned Java or another language in first year.

Published in: Software, Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

2CPP17 - File IO

  1. 1. STREAM BASED I/O Michael Heron
  2. 2. Introduction • File I/O in C++ is a relatively straightforward affair. • For the most part. • Almost all I/O in C++ is handled via streams. • Like cin and cout • Random access files also supported. • Not our focus. • Concept complicated slightly by the presence of objects. • Require a strategy to deal with object representation.
  3. 3. Stream I/O • Stream I/O is the simplest kind of I/O • Read in sequences of bytes from a device. • Write out sequences of bytes to a device • Broken into two broad categories. • Low level I/O, whereby a set number of bytes are transferred. • No representation of underlying data formats • High level I/O • Bytes are grouped into meaningful units • Such as ints, chars or strings
  4. 4. Random Access Files • Sequential files must be read in order. • Random access files permit non-sequential access to data. • System is considerably more complicated. • Must have a firm definition of all data attributes. • Issue complicated by the presence of ‘non-fixed length’ data structures. • Such as strings. • Must work out the size of a record on disk.
  5. 5. Basic File I/O - Output • Straightforward process • #include <fstream> • Instantiate an ofstream object • Use it like cout • Close in when done: #include <iostream> #include <fstream> using namespace std; int main() { ofstream out("blah.txt"); out << "Hello World" << endl; out.close return 0; }
  6. 6. Basic File I/O - Input • Same deal • Use a ifstream object • Use it like cin • Close when done #include <iostream> #include <fstream> #include <string> using namespace std; int main() { ifstream in("blah.txt"); string bleh; in >> bleh; cout << bleh; in.close(); return 0; }
  7. 7. Stream Objects • Stream objects must be declared as value objects. • Not as pointers. • Why? • Answer due to operator overloading. • Only functions on value objects. • They work through overloading the << and >> operators. • Thus, value objects only.
  8. 8. Stream Objects • The constructor for a stream object can take a second parameter. • The type of mode for the I/O • These are defined in the namespace ios: • ofstream out ("blah.txt", ios::app); • Used for specialising the type of stream. • Above sets an append. • Others have more esoteric use.
  9. 9. So Far, So Good… • Limited opportunities for expression with this system. • Need more precision on representation of data • There exist a range of stream manipulators that allow for fine-grained control over stream I/O • dec • hex • octal • setbase
  10. 10. Stream Manipulators • These work on simple screen/keyboard I/O and file I/O • They make use of the Power of Polymorphism • They are defined in the std namespace. • Inserted into the stream where needed. Acts on the stream from that point onwards. #include <iostream> #include <fstream> #include <string> using namespace std; int main() { cout << oct << 10; return 0; }
  11. 11. Stream Manipulators • Some stream manipulators are parameterized • Like setbase • These are called parameterized stream manipulators • They get defined in iomanip.h • When used, they must be provided with the parameter that specialises their behaviour. • setbase takes one of three parameters • 10, 8 or 16
  12. 12. Precision • One of the common things we want to be able to do with floating point numbers is represent their precision. • Limit the number of decimal places • This is done using the precision method and the fixed stream manipulator. • Precision takes as its parameter the number of decimal places to use.
  13. 13. Precision #include <iostream> #include <fstream> #include <string> #include <iomanip> using namespace std; int main() { float pi = 3.14159265; cout.precision (5); cout << fixed << pi; return 0; }
  14. 14. Width • We can use the width method to set the maximum field width of data. • This is not a sticky modifier • Impacts on the next insertion or extraction only. • It does not truncate data • You get the full number. • It does pad data • Useful for strings. • Defaults to a blank space. Can use the setfill modifier to change the padding character.
  15. 15. Other Stream Manipulators • showpoint • Shows all the trailing zeroes in a floating point number. • Switched off with noshowpoint • Justification • Used the parameterized setw to set the width of the of the value • Use left or right to justify • Default is right jutsification • Research these • Quite a lot to handle various purposes.
  16. 16. Object File I/O • This course is about, primarily, object orientation. • I hope you have noticed that… • As such, we should talk about how to save objects in C++. • A little bit tricky • In Java and C#, a process exists called serialization • Writes an object out to a file stream. • Flattens objects into a string of bits
  17. 17. Serialization in Java public void readFile() { bing = null; try { FileInputStream myFile = new FileInputStream ("bing.dat"); ObjectInputStream in = new ObjectInputStream (myFile); bing = (ExampleClass) in.readObject(); } catch (Exception ex) { System.out.println ("Error!"); } }
  18. 18. Serialization in C++ • C++ has no inherent mechanism for dealing with object serialization. • We need to Roll Our Own • Many different ways to do it • Text versus binary • Simple representations • Good for objects that stand alone • Complex representations • Encapsulate I/O as part of a class • Write out the capsule only.
  19. 19. Serialization in C++ • We will concentrate on the simplest possible representation here. • For objects that are stand alone. • Simplest system is to simply write out each value one at a time. • You need to set yourself a file format here. • When you read the values back in… • Set the appropriate attributes. • Very simple. • Very limited.
  20. 20. Serialization in C++ • For more complex representations, more complex processes are required. • One I like to use is to create save capsules. • Each class handles only the data it itself is responsible for. • Passes the capsule up the inheritance chain until it reaches the base class. • Use a standard data structure for this. • A hash-map is appropriate.
  21. 21. Capsule I/O • The process of de-capsuling works in a similar way. • Pass in a capsule • Extract those elements from the capsule that are relevant • Pass the capsule up the tree. • Each class must be properly designed to accommodate this. • Representation is robust • It can handle changes in file structures and inheritance trees. • Representation is relatively complex • Any break in the chain will break the representation.
  22. 22. Capsule I/O • The capsule itself is all we have to save. • We don’t actually serialize the class, we serialize the hash map. • Best to use a solid core class for this • Serialization is a fragile process. • Java permits you to just serialize a class. • Change any of the code, and the saved versions are incompatible. • Serialization also falls down on very complex objects. • Every part of an object must be serializable.
  23. 23. Save Capsule Example public SaveCapsule loadMe (SaveCapsule p) { p = super.loadMe (p); String room, region; room = p.getStringValue ("room"); region = p.getStringValue ("region"); return p; } The code that loads the HashMap creates a blank object. Here the HashMap is encapsulated in a class called SaveCapsule. It then calls loadMe on that object, passing in the save capsule. The first thing that does is pass the save capsule onto the parent for it to load from. This persists up the tree.
  24. 24. Serialization • Recommendation for this: • As simple as you can. • Complexity comes from an inheritance tree. Most complicated and robust solutions are based around solving this. • Go with simple flat file representation if possible. • No need to make things more complicated than they have to be. • Investigate other solutions only as a neccessity.
  25. 25. Parsing • We use the word ‘parsing’ to indicate taking a basic data source and turning it into a more complex representation. • As per your coursework – turn a string into something more amenable to processing. • Easy to pull a string in from a file. • Process of turning that into useable data is a parsing exercise. • Files represent persistence, not structure. • They contain the data, not its context.
  26. 26. Reading In A Paragraph • Default >> operator works by using spaces to delimit. • Pulls out one word at a time. • More awkward code needed to pull out all strings. • Even more to store them. • Remember your STL classes here. • Simplest mechanism to append to a string • In a loop
  27. 27. Reading In A Paragraph #include <iostream> #include <fstream> #include <string> #include <iomanip> using namespace std; int main() { ifstream in ("blah.txt"); string str; in >> str; while (in != NULL) { cout << str << " "; in >> str; } return 0; }
  28. 28. Summary • I/O modeled primarily in C++ as stream based I/O • Works on the same principles as cin and cout • Various ways to specialise this behaviour • Stream manipulators • Open modes • Object representation more complex than in other languages. • C++ requires a bespoke implementation of object serialization.