befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as...

39
Chapter Three 3. File Management Data, as well as programmers, should be persistent. By persistent, it means that the data should survive when the program is finished. Can you imagine if, after typing this chapter, when exited from Microsoft Word, everything typed was lost? With the programs we have written so far, this is exactly what would happen. Whatever values we have stored in variables do not persist, or survive, when the program is finished. Instead, the data is lost because the data is stored in RAM (random access memory), which is cleared when the program (or the computer) stops running. Fortunately, Microsoft Word (and most programs for that matter) has the capability to save data to a file on the computer’s hard drive or other storage medium so that data later can be retrieved when needed. That data persists after the termination of the program or even after the computer is turned off. This chapter will show you how to make your data persistent by saving it to a file. Of course, saving the data accomplishes little unless you can later retrieve it, so this chapter also will show you how to retrieve data from a file. In this chapter, we explain how to build C++ programs that create, update and process data files. We consider both sequential files and random-access files. We compare formatted- data file processing and raw-data file processing. 3.1. Streams and Files C++ views each file as a sequence of bytes. Each file ends either with an end-of-file marker or at a specific byte number recorded in a system-maintained, administrative data structure. When a file is opened, an object is created, and a stream is associated with the object. We saw that objects cin and cout are created 1

Transcript of befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as...

Page 1: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

Chapter Three3. File Management

Data, as well as programmers, should be persistent. By persistent, it means that the data should survive when the program is finished. Can you imagine if, after typing this chapter, when exited from Microsoft Word, everything typed was lost?

With the programs we have written so far, this is exactly what would happen. Whatever values we have stored in variables do not persist, or survive, when the program is finished. Instead, the data is lost because the data is stored in RAM (random access memory), which is cleared when the program (or the computer) stops running.

Fortunately, Microsoft Word (and most programs for that matter) has the capability to save data to a file on the computer’s hard drive or other storage medium so that data later can be retrieved when needed. That data persists after the termination of the program or even after the computer is turned off.

This chapter will show you how to make your data persistent by saving it to a file. Of course, saving the data accomplishes little unless you can later retrieve it, so this chapter also will show you how to retrieve data from a file.

In this chapter, we explain how to build C++ programs that create, update and process data files. We consider both sequential files and random-access files. We compare formatted-data file processing and raw-data file processing.

3.1. Streams and Files

C++ views each file as a sequence of bytes. Each file ends either with an end-of-file marker or at a specific byte number recorded in a system-maintained, administrative data structure. When a file is opened, an object is created, and a stream is associated with the object. We saw that objects cin and cout are created when <iostream> is included. The streams associated with these objects provide communication channels between a program and a particular file or device. For example, the cin object (standard input stream object) enables a program to input data from the keyboard or from other devices; the cout object (standard output stream object) enables a program to output data to the screen or other devices. Keep in mind that when we say input and output streams it is relative to the program that you are writing but not to the files that the program is used to write data permanently. To perform file processing in C++, header files <iostream> and <fstream> must be included.

We have been using the iostream standard library, which supports, among other functionalities, cin for reading from standard input (usually the keyboard), and cout for outputting to standard output (usually the monitor). The next figure clearly show the relationship between file and streams. Remember that when we say input/output it is relative to the program (i.e.,cpp file).

1

Page 2: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

Reading or writing from a file requires another standard library, fstream. The fstream standard library is included with the statement:

#include <fstream>

Both iostream and fstream have in common the word “stream.” This is no accident. Both standard libraries concern streams of bytes. The iostream library concerns streams of bytes resulting from the “io” in iostream, input and output. The fstream standard library concerns streams of bytes resulting from the “f” in fstream, a file.

The fstream header file defines three new data types:

ofstream This data type represents the output file stream—the “o” in ofstream standing for output. The direction of output is from your program out to a file. The ofstream data type is used to create files and to write information to files. It cannot be used to read files.

ifstream This data type represents the input file stream—the “i” in ifstream standing for input. The direction of input is from a file into your program. The ifstream data type is used to read information from files. It cannot be used to create files or to write information to them.

fstream This data type represents the file stream generally, and has the capabilities of both ofstream and ifstream. It can create files, write information to files, and read information from files.

2

Page 3: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

3.2. Text vs. Binary Files

If you work on a computer, you work with files. You may have worked with hundreds if not thousands of files.

A file is a collection of data, and is located on persistent storage such as a hard drive, a CD-ROM, or other storage device.

A file is referred to by a name (called a filename), which often is descriptive of the nature or contents of the file. For example, the Microsoft Word document for this chapter may be named chapter 3.

A filename usually has an extension, beginning with a period (.). For example, if the file for this chapter is named chapter13.docx, the extension is .docx.

The purpose of the file extension is to indicate the type of date in the file and the program that normally is used to access the file. Accordingly, by convention, .docx is the extension for files normally accessed by Microsoft Word; .xlsx is the extension for files normally accessed by Microsoft Excel, and so forth. One extension you may have used frequently when working with this course is .cpp, for C++ source files.

As there are many types of programs, there are many types of files, and many different file extensions. However, fundamentally, there are two types of files: text and binary.

A text file is, as the name suggests, a file that contains text. An example is a file you might create in Notepad or another plain-text editor.

The meaning of binary in a binary file is less intuitive. View a Microsoft Word document in Notepad or another plain-text editor, such as the one I used to type this chapter. You will see, in addition to the text, strange characters such as ã6, ÌL, h5, and dark vertical lines that most definitely do not appear in the text. These are formatting codes used by Microsoft Word to format the text, such as for tables, bulleted and numbered lists, and so forth.

Text files can only store text. By contrast, binary files can store other types of information, such as images, database records, executable programs, and so forth. Consequently, more complex programs, such as Microsoft Word, Excel, or Access, store data in binary files.

Text files are somewhat simpler than binary files to access, read, and write. Consequently, file access usually is introduced using text files, then continue to binary file access. This chapter deals with both text file and binary file processing.

3.3. The File Access Life Cycle

When your program accesses a file, whether to read it, write to it, or both, it goes through the following steps.

3

Page 4: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

First of all, the file first must be opened. This establishes a path of communication between the file and a stream objects in your program—fstream, ofstream, or ifstream—used to access the file. After opening the file, you should check if the file is opened successfully or not. You proceed to process the file only if it is opened successfully.

Your program then reads from, or writes to, the file (or both). This section will discuss writing to a file before reading to it, but in your program the order could be reversed. Additionally, your program may only read from a file, or only write to a file.

Finally, your program closes the file. Maintaining the path of communication between the file and the stream object in your program requires system resources, so closing the file frees those resources when they are no longer needed. Additionally, you may not be able to access the file later in your program if you did not close it after the previous access.

3.4. Text File Processing3.4.1. Opening a File

A file must be opened before you can read from it or write to it. As discussed in the introduction to this section, opening a file establishes a path of communication between the file and a stream object in your program.

3.4.1.1.Opening a File for Writing

Either the ofstream or fstream object may be used to open a file for writing. However, the ifstream object cannot be used for this purpose because it only may be used to read from a file.

Both the ofstream and fstream objects may open a file in one of two ways. The first way is using a member function named, as you might expect, open. The second alternative is using a constructor, which is explained in the “The fstream or ofstream Constructor” section later in this chapter. The first alternative, that is the open member function, is consistently used for this chapter. The second alternative, that is the constructor, is mentioned only for completeness.

The Open Member Function

Both the ofstream and fstream objects use an open member function, whose first argument is the name and location of the file to be opened. However, whether you include a second argument may depend on whether the ofstream or fstream object is calling the open member function, or whether you want to access the file in a different “mode” than the default.

First Argument—Specifying the File to Be Opened

The file to be opened for writing need not already exist. If it does not, attempting to open it for writing to it automatically will create it with the specified name at the specified location. However, whether or not the file yet exists, you need to specify a file name and location.

Accordingly, whether the ofstream or fstream object is calling the function, the first argument specifies the name and location of the file to be opened. This information may be provided by

4

Page 5: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

using either the relative path or absolute path of the file. The terms relative path and absolute path are new, so let’s discuss them now.

The relative path is the path relative to the location of your program. For example, the following statements open for writing a file, students.dat, which is in the same directory as the program:

  ofstream outfile;   outfile.open("students.dat");

By contrast, the absolute path is the path starting with the drive letter, and including each directory and subdirectory until the file is reached. For example, if the students.dat file is in the Classes subdirectory of the College directory of my C drive, it would be opened for writing, using the absolute path, as follows:

  ofstream outfile;   outfile.open("c:\\college\\classes\\students.dat");

Note Two backslashes are necessary because one backslash is used to note an escape sequence. Two backslashes is the escape sequence for one backslash

Whether you use a relative or absolute path, the argument for the open function need not be a string literal. It also may be a string variable, as in the following code fragment:

  ofstream outfile;   char filename[80];   cout << "Enter name of file: ";   cin >> filename;   outfile.open(filename);

Note As a general rule, using a relative path is preferable, particularly if the program will be used on different machines. While the location of the data file relative to the program directory may remain the same, there is no guarantee that the particular placement of the program on one computer’s directory structure will be the same as another’s.

Second Argument—File Mode

The second argument of the open member function defines the mode in which the file should be opened. One choice is whether the file should be opened for writing, reading, or both. However, there are other choices; each called a file mode flag. The following table lists the file mode flags:

5

Page 6: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

Table : File Mode Flags File Mode Flag Descriptionios::app Append mode. The file's existing contents are preserved and all output

is written to the end of the file.ios::ate If the file already exists, the program goes directly to the end of it.

Output may be written anywhere in the file. This flag usually is used with binary mode.

ios::binary Binary mode. Information is written to the file in binary format, rather than in the default text format.

ios::in Input mode. Information will be read from the file. The file will not be created if it does not exist.

ios::out Output mode. Information will be written to the file. By default, the existing file contents will be overwritten.

ios::trunk If the file already exists, its contents will be truncated, another word for deleted or overwritten. This is the default mode of ios::out.

If you use the ofstream object to open a file, you do not need any file mode flags. Indeed, the examples in the previous section did not use any file mode flags. An ofstream object may only be used to open a file for writing, and cannot be used to open a file for reading. Therefore, there is no need to specify the ios::out flag; use of that flag is implied by use of the ofstream object to open the file.

However, you may want to use one or more file mode flags with the open member function of the ofstream object if you do not want the default, which is to open the file in text rather than binary mode and overwrite rather than append to the existing file contents. One example of when you might want to append is an error log file, which keeps track of errors that may occur in a program. When a new error occurs, you don’t want to erase the history of prior errors, but rather you want to add to that history.

You can combine two or more flags when opening a file. For example, the following statements open a file in binary mode and to append rather than to overwrite. The two file mode flags are combined using the bitwise or operator (|):

  ofstream outfile;   outfile.open("students.dat", ios::binary | ios::app);

Note The bitwise or operator | is not the same as the logical or operator || even though they share the name or and the keystroke |.

While you don’t need to specify any file mode flags if you use the ofstream object to open a file, you should specify file mode flags if you use the fstream object to open a file. Whereas an ofstream object may only be used to open a file for writing and not reading, an fstream object may be used for both purposes. Therefore, you should specify whether you are using the open member function of the fstream object to open the file for writing, reading, or both.

6

Page 7: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

The following code fragment uses the open member function of the fstream object to open the file for writing only:

  fstream afile;   afile.open("students.dat", ios::out);

The fstream or ofstream Constructor (this part is put only for completeness)

You also may use the fstream or ofstream constructor to open a file for writing. A constructor is a function that is automatically called when you attempt to create an instance of an object.

An object instance is similar to a variable of a primitive data type, such as an int. For example, the following statement could be characterized as creating an instance, named age, of an integer:

  int age;

Similarly, the following statement creates an fstream instance named afile:

  fstream afile;

Object constructors may be overloaded, such that for the same object there may be a constructor with no arguments, a constructor with one argument, a constructor with two arguments, and so forth. For example, the previous statement, fstream afile, is called the no-argument constructor of the fstream object.

The following statement calls the one-argument constructor of the ofstream object, both creating an ofstream instance and opening the file students.dat for output:

  ofstream outFile(“students.dat", ios:out);

The following statement calls the two-argument constructor of the fstream object, both creating an fstream instance and opening the file students.dat for output:

  fstream aFile(“students.dat", ios:out);

In essence, declaring an ofstream (or fstream) variable in one statement and then calling the open member function in a second statement is analogous to declaring a primitive variable in one statement and then assigning it a value in a second statement, such as:

  int age;   age = 39;

By contrast, using the one or two argument ofstream (or fstream) constructor is analogous to initializing a primitive variable, such as:

  int age = 39;

7

Page 8: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

One alternative is not inherently better than the other. Usually, the specific needs of a particular program will dictate which alternative better fits your needs.

3.4.1.2.Opening a File for Reading

The discussion in the previous section concerning opening a file for writing also applies to opening a file for reading. The primary difference is that the object that calls the open member function, or whose constructor you may use, may be, in addition to an fstream object, an ifstream object instead of an ofstream object. Additionally, the file to be opened for reading must already exist. Unlike opening a file for writing, attempting to open a file for reading will not automatically create it if it does not yet exist. This issue is discussed further in the next section.

The following statements use the open member function of the ifstream object to open a file for reading:

  ifstream infile;   infile.open("students.dat");

You could accomplish the same purpose using the fstream object, specifying by a file mode flag that the file is being opened for reading only:

  fstream afile;   afile.open("students.dat", ios::in);

The following statement uses the ifstream constructor to open a file for reading:

  ifstream infile ("students.dat");

You could accomplish the same purpose using the fstream constructor, specifying in the second argument the file mode flag that the file is being opened for reading only:

  fstream afile ("students.dat", ios::in);

3.4.1.3.Opening a File for Reading and Writing

You can use the fstream object to open a file for reading and for writing. You cannot use either the ofstream or ifstream object for this purpose, as an ofstream object cannot be used to read files, and an ifstream object cannot be used to write to files.

The following code fragment uses the open member function of the fstream object for this purpose:

  fstream afile;   afile.open("students.dat", ios::in | ios::out);

Alternatively, you can use the two-argument fstream constructor:

8

Page 9: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

  fstream afile ("students.dat", ios::in | ios::out);

Both alternatives use the bitwise or operator (|) discussed in the earlier section “Second Argument—File Mode” to combine the file mode flags for input and output.

Note Combining the ios::in and ios::out flags changes expected defaults. The ios::out flag by itself causes an existing file to be overwritten, and the ios::in flag by itself requires that the file already exist. However, when the ios::in and ios::out files are used together, the file’s existing contents are preserved, and the file will be created if it does not already exist.

3.4.1.4.Checking if the File Was Opened

You should not assume that a file was successfully opened with the open member function or the constructor. There are several reasons why the file may not have been successfully opened. If the file was not successfully opened, but your code casually assumes it was and attempts to read from, or write to, the file, errors may occur.

The primary difference between opening a file for reading and for writing is that while you can write to a file that does not exist—the operating system simply creates the file—you cannot read from a file unless it already exists. Therefore, you should check if the file was opened successfully for reading before you attempt to read it.

If the file could not be opened for reading, then the value of the ifstream object that called the open function is NULL. NULL is a constant defined in several standard library files whose value is zero.

Alternatively, if the file could not be opened for reading, then the ifstream object’s fail member function returns true, which is the fail function’s return value if a file operation, in this case attempting to open a file, was not successful.

The following code illustrates the use of both checking if the ifstream object used to call the open function is NULL and whether the ifstream object’s fail member function returns true:

#include <fstream>#include <iostream>

int main (){   ifstream infile;   infile.open("students.dat");   cout << "(infile) = " << infile << endl;   cout << "(infile.fail()) = " << infile.fail() << endl;   return 0;}

If the students.dat file does not yet exist, the output would be

(infile) = 00000000

9

Page 10: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

(infile.fail()) = 1

However, if there was a file named students.dat in the same directory as your program, then the output would be

(infile) = 0012FE40(infile.fail()) = 0

The value, 0012FE40, is the address of the ifstream variable infile, and of course could be different if you run this program.

Unlike an ifstream object, an ofstream object that attempts to open a file that does not yet exist is not NULL, and its fail member function would return false, because the operating system will create the file if it does not already exist. However, opening a file for writing is not always successful. For example, before you run the following program, create a file named students.dat in the same directory as your program but, through its properties, check read only:

#include <fstream>#include <iostream>using namespace std;

int main (){   ofstream outfile;   outfile.open("students.dat");   cout << "( outfile) = " << outfile << endl;   cout << "( outfile.fail()) = " << outfile.fail() << endl;   return 0;}

The following output reflects that the ofstream object is NULL, and its fail function returns true, because you cannot open for writing a file that is read only.

  (outfile) = 00000000   (outfile.fail()) = 1

If you cannot open a file for reading or writing, then you do not want to proceed to execute the code that reads from, or writes to, the file. Instead, you may want to stop execution of the function, as in the following code fragment:

  ifstream infile;   infile.open("students.dat");   if (infile.fail())   {      cout << "Error in opening file for reading";      exit(0);   }   // code to read from file

10

Page 11: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

Note: For purposes of avoiding repetitive code, some of the following code in the following code in this chapter omits checking if a file was opened successfully. However, you should include it in every program that involves file management.

3.4.2. Closing a File

Of course, you are not going to close a file as soon as you open it. You will read or write to the file first. However, closing a file is relatively simple, so I will discuss this issue out of order before discussing the more complex subjects of writing to, and reading from, a file.

You should close a file when you are finished reading or writing to it. While the file object will be closed when the program ends, your program’s performance will be improved if you close a file when you are finished with it because each open file requires system resources. Additionally, some operating systems limit the number of open “handles” to files. Finally, you will avoid a “sharing” problem caused by trying in one part of your program to open a file that in another part of the program previously was opened but not closed.

You close a file using the close member function, which takes no arguments. The following example closes a file opened for writing:

  ofstream outfile;   outfile.open("students.dat");   // do something   outfile.close();

The same syntax applies to closing a file for reading.

  ifstream infile;   infile.open("students.dat");   // do something   infile.close();

3.4.3. Writing to a File

You output or write information to a file from your program using the stream insertion operator (<<) just as you use that operator to output information to the screen. The only difference is that you use an ofstream or fstream object instead of the cout object.

The following program writes information inputted by the user to a file named students.dat, which is created if it does not already exist.

#include <fstream>#include <iostream>using namespace std;

int main (){   char data[80];   ofstream outfile;

11

Page 12: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

  outfile.open("students.dat");   cout << "Writing to the file" << endl;   cout << "===================" << endl;   cout << "Enter class name: ";   cin.getline(data, 80);   outfile << data << endl;   cout << "Enter number of students: ";   cin >> data;   cin.ignore();   outfile << data << endl;   outfile.close();   return 0;}

The input and output could be

Writing to the file===================Enter class name: Programming SimplifiedEnter number of students: 32

Open the file in a plain-text editor such as Notepad. The contents with the preceding sample input would be as follows:

Programming Simplified32

The statement that wrote to the file included the endl keyword:

  outfile << data << endl;

The reason is to write the name of the class (“Programming Simplified”) to a different line than the number of students, 32. Otherwise, the file contents would be

Programming Simplified32

You instead could have used an fstream object to write to the file. You would have changed the data type of outfile from ofstream to fstream and then changed the call to the open method to include two arguments:

  fstream outfile;   outfile.open("students.dat", ios::out);

Alternatively, you could have used the fstream constructor:

  fstream outfile ("students.dat", ios::out);

If you want to append, you only need to add an ios:app flag to the second argument of the constructor or the open member function using the bitwise or operator (|).

3.4.4. Reading from a File

12

Page 13: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

You input or read information from a file into your program using the stream extraction operator (>>) just as you use that operator to input information from the keyboard. The only difference is that you use an ifstream (or fstream) object instead of the cin object.

The following program builds on the previous one. After writing information inputted by the user to a file named students.dat, the program reads information from the file and outputs it onto the screen:

#include <fstream>#include <iostream>using namespace std;

int main (){   char data[80];   ofstream outfile;   outfile.open("students.dat");   cout << "Writing to the file" << endl;   cout << "===================" << endl;   cout << "Enter class name: ";   cin.getline(data, 80);   outfile << data << endl;   cout << "Enter number of students: ";   cin >> data;   cin.ignore();   outfile << data << endl;   outfile.close();   ifstream infile;   cout << "Reading from the file" << endl;   cout << "=====================" << endl;   infile.open("students.dat");   infile >> data;   cout << data << endl;   infile >> data;   cout << data << endl;   infile.close();   return 0;}

Sample input and output:

Writing to the file===================Enter class name: ProgrammingEnter number of students: 32Reading from the file=====================Programming32

Reading a Line of a File

With the same program, try entering a class name with an embedded space. The following is some sample input and output:

13

Page 14: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

Writing to the file===================Enter class name: Programming SimplifiedEnter number of students: 32Reading from the file=====================ProgrammingSimplified

The following are the contents of the file after the inputted data was written to it:

Programming Simplified32

The first read of the file did not read the first line of the file, “Programming Simplified.” Instead, the first read of the file only read the word “Programming” and then stopped. Consequently the second line of the program read the remainder of the first line of the file, “Simplified,” instead of the number of students.

The ifstream object together with the stream extraction operator reads the file sequentially, starting with the first byte of the file. The first attempt to read the file starts at the beginning of the file and goes to the first whitespace character (a space, tab, or new line) or the end of the file, whichever comes first. The second attempt starts at the first printable character after that whitespace, and continues to the next whitespace character or the end of the file, whichever comes first.

The first read attempt only read “Programming,” not “Programming Simplified,” because the read stopped at the whitespace between “Programming” and “Simplified.” The second attempt read “Simplified.” There were no further read attempts, so the number of students, 32, was never read.

This should seem like déjà vu. We encountered a similar issue in Chapter 2 using the cin object with the stream extraction operator (>>). As in Chapter 2 with the cin object, the solution is to use getline.

If you are working with array of characters (or strings), then you should use the getline member function. The only difference between using the getline member function here and in Chapter 2 is that here the getline member function is called by an ifstream or fstream object instead of a cin object. Accordingly, we need to replace the two calls to infile >> data with the following:

  infile.getline(data, 80);

The following modification of the previous program uses the getline function with strings:

#include <fstream>#include <iostream>#include <string>

int main ()

14

Page 15: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

{   char data[80];   ofstream outfile;   outfile.open("students.dat");   cout << "Writing to the file" << endl;   cout << "===================" << endl;   cout << "Enter class name: "; cin.getline(data,80);   outfile << data<< endl;   cout << "Enter number of students: ";   cin >> data;   cin.ignore();   outfile << data<< endl;   outfile.close();   ifstream infile;   cout << "Reading from the file" << endl;   cout << "=====================" << endl;   infile.open("students.dat"); infile.getline(data,80);   cout << data << endl; infile.getline(data,80);   cout << data << endl;   infile.close();   return 0;}

As the following sample input and output reflects, the first read now reads the entire first line of the file even when that line contains embedded spaces:

Writing to the file===================Enter class name: Programming SimplifiedEnter number of students: 32Reading from the file=====================Programming Simplified32

Looping Through the File

In the previous program, exactly two read attempts were made because we knew there were two lines of data in the file, no more, no less. However, often we may not know the number of pieces of data to be read. All we want is to read the file until we have reached the end of it.

The ifstream object has an eof () function, eof () being an abbreviation for end of file. This function, which takes no arguments, returns true if the end of the file has been reached, and false if otherwise.

However, the eof() function is not as reliable with text files as it is with binary files in detecting the end of the file. The eof () function’s return value may not accurately reflect if the end of the file was reached if the last item in the file is followed by one or more whitespace characters. This is not an issue with binary files since they do not contain whitespace characters.

15

Page 16: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

A better choice is the fail member function, discussed in the earlier section “Checking if the File Was opened.” The following code fragment shows how to use the eof() member function in reading a file until the end of the file is reached:

  ifstream infile;   infile.open("students.dat");   infile >> data;   while(!infile.eof())   {     cout << data;     infile >> data;   }   infile.close();

Note: You should read once from the file before you enter into the while loop and read again at the end of the while loop. Also notice the exclamatory mark (“!”) inside the while loop.

Q. Can we use for loop to do the same task? If not, why?

The preceding code fragment has two infile >> data statements, one before the loop begins, the other inside the loop. The reason is that the end of file is not detected until after a read attempt is made. Thus, if the infile >> data statement before the loop was omitted and the file was empty, the cout << data statement would execute before an attempt was made to detect if the end of file had been reached.

Note A do while loop could be used instead of a while loop. This would dispense with the need to check for end of file before entering the loop, but add the requirement to check inside the loop if (using an if statement) end of file had been reached. This is the usual tradeoff between while and do while loops.

Modifying the previous program, the code now would read

#include <fstream>#include <iostream>#include <string>

int main (){   char data[80];   ofstream outfile;   outfile.open("students.dat");   cout << "Writing to the file" << endl;   cout << "===================" << endl;   cout << "Enter class name: "; cin.getline(data,80);   outfile << data<< endl;   cout << "Enter number of students: ";   cin >> data;   cin.ignore();   outfile << data<< endl;   outfile.close();

16

Page 17: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

  ifstream infile;   cout << "Reading from the file" << endl;   cout << "=====================" << endl;   infile.open("students.dat"); infile.getline(data,80);   while(!infile.eof())   {      cout << data << endl; infile.getline(data,80);   }   infile.close();   return 0;}

3.4.5. File Stream Objects as Function Arguments

Chapter 1 explained how you can use functions to make your code modular. In that spirit, let’s rewrite the previous program to add two functions, each to be called from main: writeFile to open a file for writing using an ofstream object, and readFile to open a file for reading using an ifstream object.

#include <iostream.h>#include <fstream.h>#include <conio.h>#include <string.h>#include <stdlib.h> //to use exit(0)void writeFile (ofstream& outfile, char strFile[]);void readFile (ifstream& infile, char strFile[])

int main (){ clrscr();   ofstream outfile; ifstream infile;   writeFile(outfile, "c:\\students.dat"); readFile(infile, "c:\\students.dat"); return 0;}

void writeFile (ofstream& outfile, char strFile[]){   char data[80]; outfile.open(strFile);   if (outfile.fail()) {      cout << "File could not be opened for writing\n";      cout << "Program terminating\n"; getch();      exit(0) }

  else {      cout << "Writing to the file" << endl;      cout << "===================" << endl;

17

Page 18: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

     cout << "Enter class name: "; cin.getline(data,80);      outfile << data<< endl;      cout << "Enter number of students: ";      cin >> data;      cin.ignore();      outfile << data<< endl;      outfile.close(); }}

void readFile (ifstream& infile, char strFile[]){   char data[80]; infile.open(strFile); if (file.fail()) {      cout << "File could not be opened for reading\n";      cout << "Program terminating\n"; getch();      exit(0) }   else {      cout << "Reading from the file" << endl;      cout << "=====================" << endl; infile.getline(data,80);      while(!infile.eof())      {         cout << data << endl; infile.getline(data,80):      }      infile.close(); }

}

For each function, the file stream object is passed by reference instead of by value even though neither function changes the contents of the file. The reason is that the internal state of a file stream object may change with an open operation even if the contents of the file may not change.

A program that accomplishes the same job as above, but using fstream instead of ifstream and ofstream is given below. Note that this program is the one used in lab.#include <iostream.h>#include <fstream.h>#include <conio.h>#include <string.h>#include <stdlib.h>void writeFile (fstream & afile, char fileName[]);void readFile (fstream & afile, char fileName[]);int main (){ clrscr(); fstream afile; char* fileName;//you can’t use array here.

18

Page 19: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

fileName ="c:\\students.dat"; writeFile(afile, fileName); readFile(afile, fileName; getch(); return 0;}void writeFile (fstream & afile, char fileName[]){ char data[80]; afile.open(fileName,ios::out); if (!afile) { cout << "File could not be opened for writing\n"; cout << "Program terminating\n"; getch(); exit(0); } else { cout << "Writing to the file" << endl; cout << "===================" << endl; cout << "Enter student name: "; cin.getline(data,80); afile << data<< endl; cout << "Enter height of student: "; cin >> data; cin.ignore(); afile << data<< endl; afile.close(); }}void readFile (fstream & afile, char fileName[]){ char data[80]; afile.open(fileName,ios::in); if (!afile) { cout << "File could not be opened for reading\n"; cout << "Program terminating\n"; getch(); exit(0); } else { cout << "Reading from the file" << endl; cout << "=====================" << endl; afile.getline(data,80); while(!afile.eof()) { cout << data << endl;

afile.getline(data,80); } afile.close(); }

}

19

Page 20: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

3.5. Binary File Management

At this point you should be able to identify the difference between text file and binary file. This was discussed in section 3.2. Also in the previous section (section 3.4) we have seen how to process text file in detail. In this section, binary file management will be discussed in detail. Note that binary files, rather than text files, are used in many real life projects that incorporate structures and other advanced data structures.Fortunately, binary file processing is almost similar with text file processing. All the file access life cycle steps are also followed here. The only difference here in binary file processing is that: first, during the file opening step ios::binary mode should be concatenated; second, unlike to text files processing, the the reading and writing process involves write and read member methods. This is because the >>, getline and << operators do not work properly and efficiently for binary files because binary files has no format including new line and may not be read in forwarding manner .Here is a sample code that shows how write to text file and binary filesfstream aFile;aFile.open(my_file, ios::out);aFile << my_double;aFile.close()

for binary file.fstream aFile;aFile.open(my_file, ios::out | ios::binary);aFile.write( (char*)&my_double, sizeof(double));aFile.close()

C++ File I/O with binary files using fstream class is a simple task. fstream class has the capability to do both Input as well as Output operations i.e., read and write. Operating systems store the files in binary file format. Computers can deal with only binary numbers. But binary files are not readable by humans. Our level of comfort lies only with proper ASCII or UNICODE characters. As mentioned earlier, binary files are important for reading and writing structures in a file since structures can contain variables of different data types. Here I used the following structure for demonstrating all examples in this section. Note that this structure is part of Item structure given in your project. The examples are not a complete program to be used in the project that students are expected to use it only as a starting point.struct Item {

char iID[10];char iName[20];char category[10];int qtyfloat price;

};

20

Page 21: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

3.5.1. Write operations to Binary File

There are some important points to be noted while doing a write operation.

The file has to be opened in output and binary mode using the flags ios::out (output mode) and ios::binary (binary mode). You can concatenate other modes according to your interest.

The function write takes two parameters. The first parameter is of type char* for the data to be written and the second is of type int asking for the size of data to be written to the binary file.

File has to be closed at the end.

Here is sample code for writing to a binary file write.

void addItem(fstream & aFile, char fileName[]){ Item i; char choice; aFile.open(fileName,ios::out|ios::app|ios::binary); if(!aFile) { cout<<fileName<<" file opening error for writing"<<endl; cout<<"program terminating"<<endl; getch(); exit(0); } else { do { cout<<"enter item ID: "; cin>>i.iID; cin.ignore(); cout<<"enter item Name: "; cin.getline(i.iName,20); cout<<"enter category: "; cin.getline(i.category,10); cout<<"enter quantity: "; cin>>i.qty; cin.ignore(); cout<<"enter price: "; cin>>i.price; cin.ignore(); aFile.write((char *)&i,sizeof(i)); cout<<"continuea?(y/n): "; cin>>choice; cin.ignore(); }while(toupper(choice)=='Y');//include ctype.h for toupper() aFile.close(); }}

21

Page 22: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

The above function writes some data to a file. The file is opened in output and binary mode with ios::out and ios::binary. There is one more specifier ios::app, which tells the Operating system that the file is also opened in append mode. This means any new set of data will be appended to the end of file. Also the write function used above, needs the first parameter as a pointer a character type. So we use a type converter/cast to typecast the structure into (char*) type.

3.5.2. Read operations from Binary File

This also has a similar flow of operations as above. The only difference is to open the file using ios::in and ios::binary, which opens the file in read mode in binary format.

//here is sample code for reading from binary file. Try to handtrace and understand the code. void dispAvailableItem(fstream & aFile, char fileName[]){ Item i; aFile.open(fileName,ios::in|ios::binary); if(!aFile) {

cout<<fileName<<" file opening error for reading"<<endl;cout<<"program terminating"<<endl;getch();exit(0);

} else { aFile.read((char*)&i,sizeof(i)); int counter=0;//to trace the position of each item strucure while(!aFile.eof()) { counter++; cout<<"\n\nItem "<<counter<<" Data"<<endl<<endl; cout<<"item id: "<<i.iID <<endl; cout<<"item name: "<<i.iName<<endl; cout<<"category: "<<i.category<<endl; cout<<"quantity: "<<i.qty<<endl; cout<<"price: "<<i.price<<endl; aFile.read((char*)&i,sizeof(i)); } aFile.close(); }}

3.6. Random Access File

“Random" means you can access whatever element at whatever position in whatever order (essentially, you can jump-around into the sequence). "Sequential" means that to access a given position you must first scan whatever preceding

22

Page 23: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

A file on tape is stored and read sequentially. A file on disk is atomically read sequentially, but the unit of data (the disk clusters) can be seek individually, so allowing for a random access. A memory stored sequence has native random access.

"Random" means you can get any part of the file in any order. So for example, you can read the middle part before the start."Sequential" means you must first read the first part of the file, before reading second, then third etc...

It is how you access the file from your application that makes it "random" or "sequential". The format of the file may be optimized for one kind of access or the other, for example text is typically "sequential" while binary files tend to be highly "random". The following figure shows the difference between sequential and random access.

A better term for "random access" would be "direct access," since the hardware allows you to retrieve data anywhere on the disc. It is quite useful! as for "sequential access," the hardware is only capable of running through a each piece of data from start to end, and although it may be faster to sequentially retrieve data that is near the beginning of that sequence, it can become incredibly slow if it you want to retrieve a piece of data near the finish of that sequence, since the process has to iterate through each piece of information.

Note: Even when an application reads records sequentially, if the file is fragmented throughout the disk or disks, the I/O will not be sequential. If the disk-transfer rate on a sequential or mostly sequential read operation deteriorates over time, run Disk Defragmenter on the disk and test again. When fragmentation occurs, data is not organized in contiguous clusters on the disk. Fragmentation slows performance because back-and-forth head (of the disc) movement is slow.

In all the previous sections, files were accessed sequentially. For example, the addItem() and dispAvailableItem()functions use sequential access. The addItem() function always write at

23

Page 24: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

the end sequentially (using ios::app) and the dispAvailableItem() function reads the item sequentially. However, there are times where files should be accessed in a random access style. For example, you may want to modify the 10th item in your item file. This can be done by directly going to the 10th item, instead of sequentially moving to the 10th item beginning from the first item. Random access file is very useful for binary files (having structure) than text files. This is because text file has format like new line and read in a forward progress (like you read a book.) that makes the sequential access better. Hence, all the examples in this section use binary files with structure (particularly Item structure).

3.6.1. The file pointer

Each file stream class contains a file pointer that is used to keep track of the current read/write position within the file. When something is read from or written to a file, the reading/writing happens at the file pointer’s current location. By default, when opening a file for reading (ios::in) or writing (ios::out), the file pointer is set to the beginning of the file. However, if a file is opened in append mode (ios::app), the file pointer is moved to the end of the file, so that writing does not overwrite any of the current contents of the file. ios::ate is used for moving the file pointer anywhere in the file. Keep in mind that there are two independent pointers, one for reading and the other for writing. Moving the position of the read pointer have no effect on the positiont of write pointer. Note also that to move the pointers any where in the file, you should use ios::ate instead of ios::app.

Random file access with seekg() and seekp()So far, all of the file access we’ve done has been sequential — that is, we’ve read or written the file contents in order. However, it is also possible to do random file access — that is, skip around to various points in the file to read it’s contents. This can be useful when your file is full of records, and you wish to retrieve a specific record. Rather than reading all of the records until you get to the one you want, you can skip directly to the record you wish to retrieve.Random file access is done by manipulating the file pointer using the seekg() function (for input-reading) and seekp() function (for output-writing). In case you are wondering, the g stands for “get” which is read pointer and the p for “put” which is write pointer.The seekg() and seekp() functions take two parameters. The first parameter is an offset that determines how many bytes to move the file pointer. The offset should be a variable of type long. The second parameter is an ios flag that specifies what the offset parameter should be offset from.

ios seek flag Meaning

ios::beg The offset is relative to the beginning of the file (default)

ios::cur The offset is relative to the current location of the file pointer

Ios::end The offset is relative to the end of the file

24

Page 25: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

A positive offset means move the file pointer towards the end of the file, whereas a negative offset means move the file pointer towards the beginning of the file.

Here are some examples:

aFile.seekg(14, ios::cur); // move forward 14 bytes for reading from current reading position,

aFile.seekg(-18, ios::cur); //move backwards 18 bytes for reading from current reading position

aFile.seekg(22, ios::beg); // move 22 byte for reading from beginning (actually read the 23rd byte) from beginning

aFile.seekg(24); // move 24 byte for reading (actually your read the 25th byte) from begining aFile.seekg(-28, ios::end); // move to the 28th byte before end of the file

Moving to the beginning or end of the file is easy:

aFile.seekg(0, ios::beg); // move the read pointer to the beginning of file aFile.seekg(0, ios::end); // move read pointer to the end of file

Note: each byte in a file is indexed beginning from zero.The above table shows you how to move the read pointer in a file using seekg(). To move the write pointer in a file, replace seekp() instead of seekg() in the above table. The best way to understand how seekp() and seekg() work is through practicing in compiler.Let’s do an example using seekg() and the input file we created in the last lesson. That input file looks like this:Suppose a file called “Sample.dat” contains the following text.This is line 1This is line 2This is line 3This is line 4

Here is an example program that demonstrate seekg(), carefully handtrace and try to determine its output and compare your result with the one given below.

1

2

3

4

5

6

7

8

int main()

{

fstream aFile;

aFile.open("Sample.dat", ios::in);

if (!aFile)

{

cout << "file can’t be opened for reading" << endl;

getch();

25

Page 26: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

exit(0);

}

char strData[80];

aFile.seekg(5); // move to 5th character

// Get the rest of the line and print it

aFile.getline(strData,80);

cout << strData << endl;

aFile.seekg(8, ios::cur); // move 8 more bytes into file

// Get rest of the line and print it

aFile.getline(strData,80);

cout << strData << endl;

aFile.seekg(-12, ios::end);//move 12 bytes before end of file

// Get rest of the line and print it

aFile.getline(strData,80);

cout << strData << endl;

return 0;

}

Output is line 1line 2is is line 4

Two other useful functions are tellg() and tellp(), which return the absolute position (relative to the begging of a file) of the file pointer. This can be used to determine the size of a file:

fstream aFile;

aFile.open("Sample.dat",ios::in);

aFile.seekg(0, ios::end); // move to end of file

cout << aFile.tellg();

output62

This includes additional 2 byte for each new line character at the end of the three lines (i.e., additional 3* 2 bytes = 6 bytes).Which is how long sample.dat is in bytes (assuming a carriage return after the last line).Now it is time to demonstrate random access using the previous examples. For example, you are asked to modify the 10th item in your item file. Assume your item file contains 25 items. Here two methods are used to do this task. In the first method, you can use random access to modify the 10th item directly, but this requires you that you should know the position (at least input the location from user) where the item that you want to modify is located in the file before making the modification using random access.

26

Page 27: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

In the second method, to modify an item, first search for the items that you want to modify in the item file using a search key (for example, based on an item id). One the matching item is found, modify it and write it back in to the file in the same position it was found. However, this can’t be said purely random access because the search may involve sequentially accessing each file to find the mathing item. In addition, both methods involve moving the file pointers anywhere in the file. Hence, ios::ate should be used instead of ios::app. Both methods are demonstrated here using sample code. Students should carefully handtrace through each line of the programs to understand the above concept of each method. As a final point, don’t forget to open the file in ios::in|ios::out mode (in addition to other mode) if you want to read and write at the same time.To demonstrate the above concept, suppose some one asks you to write a function that modifies an item in the item file mentioned previously. You can use the first or the second method to do the task. Don’t forget to open the item file in ios::in|ios::out|ios::ate|ios::binary mode. This is because modifying an item requires you to both read, write, to move the file pointer any where inside the item file and to read and write binary data.The next program modifies an item in an item file. The user inputs the id of an item to be modified. This example demonstate the second method mentioned above. Hand trace through the program until you understand it to the level you can write similar code independently.void modifyItemByID(fstream& aFile, char fileName[]){ Item i; //here both ios::in and ios::out are used //here also ios::ate should be used, not ios::app aFile.open(fileName,ios::in|ios::out|ios::ate|ios::binary); if(!aFile) { cout<<fileName<<" file opening error for writing/reading"<<endl; cout<<"program terminating"<<endl; getch(); exit(0); } else { char itemID[10]; //similar with item id in item structure cout<<"\nenter item id to modify: "; cin>>itemID; long counter=0;//this should be long aFile.read((char*)&i,sizeof(i)); while(!aFile.eof()) { if(strcmp(i.iID,itemID)==0)

{ cout<<"enter new item ID: "; cin>>i.iID; cin.ignore(); cout<<"enter new item Name: "; cin.getline(i.iName,20); cout<<"enter new category: "; cin.getline(i.category,10); cout<<"enter new quantity: "; cin>>i.qty; cin.ignore(); cout<<"enter new price: ";

27

Page 28: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

cin>>i.price; cin.ignore(); aFile.seekp(counter*sizeof(i),ios::beg); aFile.write((char*)&i,sizeof(i));}aFile.read((char*)&i,sizeof(i));counter++;

}//end of while loop aFile.close(); }//end of else}

The next program also modifies an item in an item file. However, in this method, the user inputs the position of an item to be modifired in the item file instead of a search key, like the id of an item to be modified. This example demonstate the first method mentioned earlier. Hand trace through the program until you understand it to the level you can write similar code independently.void modifyItemByPostion(fstream& aFile, char fileName[]){ Item i; long position;//long is a data type //here also ios::ate should be used, not ios::app aFile.open(fileName,ios::out|ios::ate|ios::binary); if(!aFile) { cout<<fileName<<" file opening error for writing/reading"<<endl; cout<<"program terminating"<<endl; getch(); exit(0); } else { cout<<"\nenter item postion to modify: "; cin>>position; cout<<"enter new item ID: "; cin>>i.iID; cin.ignore(); cout<<"enter new item Name: "; cin.getline(i.iName,20); cout<<"enter new category: "; cin.getline(i.category,10); cout<<"enter new quantity: "; cin>>i.qty; cin.ignore(); cout<<"enter new price: "; cin>>i.price; cin.ignore(); aFile.seekp((position-1)*sizeof(i),ios::beg); aFile.write((char*)&i,sizeof(i)); aFile.close(); }}

3.7. Buffers and Synchronization

28

Page 29: befetrin.files.wordpress.com  · Web viewChapter Three. File Management. Data, as well as programmers, should be persistent. By persistent, it means that . the data should survive

When we operate with file streams, these are associated to an internal buffer object of type streambuf. This buffer object may represent a memory block that acts as an intermediary between the stream and the physical file. For example, with an ofstream, each time the member function put (which writes a single character) is called, the character may be inserted in this intermediate buffer instead of being written directly to the physical file with which the stream is associated.

The operating system may also define other layers of buffering for reading and writing to files.When the buffer is flushed, all the data contained in it is written to the physical medium (if it is an output stream). This process is called synchronization and takes place under any of the following circumstances:

When the file is closed: before closing a file, all buffers that have not yet been flushed are synchronized and all pending data is written or read to the physical medium.

When the buffer is full: Buffers have a certain size. When the buffer is full it is automatically synchronized.

Explicitly, with manipulators: When certain manipulators are used on streams, an explicit synchronization takes place. These manipulators are: flush and endl.

Explicitly, with member function sync(): Calling the stream's member function sync() causes an immediate synchronization. This function returns an int value equal to -1 if the stream has no associated buffer or in case of failure. Otherwise (if the stream buffer was successfully synchronized) it returns 0.

Students are expected to experiment synchronization with flush() and sync() function by modifying the program used in the previos sections.

29