3-8) File Input and Output

This section shows how the file streams are used to do sequential and random access file I/O

For file I/O you must include the <fstream> library which defines ifstream and ofstream streams that enable character (char) input from, and output to, files.

Files are opened by creating objects of type ifstream or ofstream.

Files must be closed when no longer needed (fstream destructor will close will).


Sample Code


Sequential File Access

 

Writing Strings to a File

 

The overloaded operator<<() can be used to write character strings to a file just like it is used to write character strings to cout
 

/* This program demonstrates writing strings to an output file
*/

#include <iostream>
#include <fstream>

using namespace std;

void main(){

    //create file stream on output file
    char * fileName = "phoneData.txt";

    ofstream file(fileName, ios::out); //ios::out -> output, ios::app -> append (defined in std)

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }

    cout << "Please enter names and phone numbers one line at time, for example\n";
    cout << "John Smith 234-7689\n";
    cout << "enter <crtl>-z to end\n";

    const int MAX_SIZE = 80;
    char firstName[MAX_SIZE];
    char lastName[MAX_SIZE];
    char phoneNumber[MAX_SIZE];

    //read data from user via cin and write it to the text file
    while(cin >> firstName >> lastName >> phoneNumber){
        file << firstName << " " << lastName << " " << phoneNumber << "\n";
    }

    file.close(); //fstream destructor will also close the file

}

 


Reading Strings to a File

 

The overloaded operator>>() can be used to read character strings from a file like it is used to write character strings from cin
 

/* This program demonstrate writing strings to an output file
*/


#include <iostream>
#include <fstream>
#include <iomanip> //for setw() stream method

using namespace std;

void main(){

    /*
    read the contents of a file whose data is expected to be in the form:
    Louis Johnson 534-6578
    John West 456-1112
    Anne Smith 567-1234
    */


    char * fileName = "phoneData.txt";

    ifstream file(fileName, ios::in); //ios::in -> input

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }


    const int MAX_SIZE = 80;
    char firstName[MAX_SIZE];
    char lastName[MAX_SIZE];
    char phoneNumber[MAX_SIZE];

    //read data from user via cin and write it to cout in columns
    //Note std::left means left justify
    // std::right means right justify
    // std::setw(x) means set witdth of field

    cout << "contents of file: " << fileName << "\n";
    cout << "=================================\n";

    while(file >> firstName >> lastName >> phoneNumber){
        cout << left << "read: " << setw(10) << firstName << setw(10) << lastName << phoneNumber << "\n";
    }

    cout << "...closing input file\n";
    file.close(); //fstream destructor will also close the file

}


 


Writing Bytes to a File

 

The ofstream's write method is used to write bytes to a file.

write(const char * p, int n) will write to the file the  n bytes (or chars) that are pointed to by p.

In this example the bytes are used to represent a binary version of an Student object. Notice how the Student class has been designed so that all Student objects occupy the same amount of memory (regardless of length of the student's name).

 

//file student.h

class Student{
private:

//student memory is designed so that each student objects occupies
//the same amount of memmory regardless of the length of there first
//and last name


    int studentNumber;
    char firstName[20];
    char lastName[20];

public:
    Student(){
        studentNumber = -1;
        strcpy(firstName, "unknown");
        strcpy(lastName,"unknown");
    }

    Student(const char * fName, const char * lName, int stdNumber){
        studentNumber = stdNumber;
        strcpy(firstName, fName);
        strcpy(lastName,lName);
    }

    void printOn(ostream & stream) const {
        stream << firstName << " " << lastName << " #" << studentNumber;
    }

    int getStudentNumber(){return studentNumber;}
};

ostream & operator<<(ostream & stream, const Student & s){
        s.printOn(stream);
        return stream;
}

 

//file ex3_fstream_output2.cpp
/* This program demonstrates writing students to a file as
constant size records which will accommodate random access later
*/


#include <iostream>
#include <fstream>

using namespace std;

#include "student.h"

void main(){

    //create file stream on output file
    char * fileName = "studentData.txt";

    ofstream file(fileName, ios::out); //ios::out -> output, ios::app -> append (defined in std)

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }

    cout << "Please enter students firstName, LastName and student number, for example\n";
    cout << "John Smith 100234587\n";
    cout << "enter <crtl>-z to end\n";

    const int MAX_SIZE = 80;
    char firstName[MAX_SIZE];
    char lastName[MAX_SIZE];
    int studentNumber;

    Student students[100];
    int numberOfStudents = 0;

    //read data from user via cin and write it to the text file
    while(cin >> firstName >> lastName >> studentNumber){
        Student student(firstName, lastName, studentNumber);
        students[numberOfStudents++] = student;

        //cast student object to a bunch of char's (binary image of student)
        //and write out that number of bytes (chars)

        file.write( (const char *) &student, sizeof(student) );

        //or better yet:
        //file.write( reinterpret_cast<const char *>( &student), sizeof(student) );

    }

    file.close(); //fstream destructor will also close the file

    cout << "\nStudents:\n";
    cout << "==========\n";
    for(int i=0; i<numberOfStudents; i++)
        cout << students[i] << "\n";

}


 


Reading Bytes from a File

 

The ifstream's read method is used to read bytes to a file.

read(char * p, int n) will read from the file n bytes (or chars) and put them in memory pointed to by p.

In this example the bytes representing a binary version of a Student object's data  are read into the memory of a Student object. In this example all Student objects are assumed to occupy the same amount of memory.

 


/* This program demonstrates reading in student objects that
have been stored as constant size binary records
*/


#include <iostream>
#include <fstream>

using namespace std;

#include "student.h"

void main(){

    /*
    read the contents of a file whose data is expected to be constant
    size Student records
    */


    char * fileName = "studentData.txt";

    ifstream file(fileName, ios::in); //ios::in -> input

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }


    Student student;

    cout << "contents of file: " << fileName << "\n";
    cout << "=================================\n";

    int i=0;
    while(file && !file.eof()){

        file.read( (char *) &student, sizeof(student) );

        //or better:
        //file.read( reinterpret_cast<char *>( &student), sizeof(student) );


        //if statement is to ensure that last student is not printed twice
        if(!file.eof()) cout << student << "\n";

    }

    cout << "...closing input file\n";
    file.close(); //fstream destructor will also close the file

}


 


Writing Random Access Data to a File

 

A ofstream maintains an offset, or "put-pointer",  which indicates where the next byte will be written by the write method.

The ofstream method long tellp() returns the current position of the put pointer.

The ofstream method seekp(int n) sets the put offset to n bytes past the start of the stream.
The ofstream method seekp(int n, ios::beg) set the put offset to n bytes past the start of the stream.
The ofstream method seekp(int n, ios::cur) sets the put offset to n bytes past the current put offset.
The ofstream method seekp(int n, ios::end) sets the put offset to n bytes before the end of file.

 

/* This program demonstrates writing students to a file as
constant size records in a random access manner
*/


#include <iostream>
#include <fstream>

using namespace std;

#include "student.h"

void main(){

    //create file stream on output file
    char * fileName = "studentRecords.txt";

    ofstream file(fileName, ios::out); //ios::out -> output, ios::app -> append (defined in std)

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }


    int numberOfRecords = 100;
    Student student;

    //Write a collection of "blank" student records as place holders in the file
    for(int i=0; i<numberOfRecords; i++){
        file.write( (const char *) &student, sizeof(student) );
    }


    cout << "Please enter index[0..." << numberOfRecords << "], students firstName, LastName and student number, for example\n";
    cout << "10 John Smith 100234587\n";
    cout << "enter <crtl>-z to end\n";

    const int MAX_SIZE = 80;
    int recordIndex;
    char firstName[MAX_SIZE];
    char lastName[MAX_SIZE];
    int studentNumber;

    //read data from user via cin and write it to the text file
    while(cin >> recordIndex >> firstName >> lastName >> studentNumber){
        Student aStudent(firstName, lastName, studentNumber);

        if(recordIndex >= 0 && recordIndex < numberOfRecords){

            //position the file put-pointer to the correct record
            file.seekp(recordIndex * sizeof(aStudent));

            //cast student object to a bunch of char's (binary image of student)
            //and write out that number of bytes (chars)

            file.write( (const char *) &aStudent, sizeof(aStudent) );

            //or better yet:
            //file.write( reinterpret_cast<const char *>( &student), sizeof(student) );

        }
    }

    cout << "....closing file " << fileName << "\n";
    file.close(); //fstream destructor will also close the file

}


Reading Random Access Data to a File

 

A ifstream maintains an offset, or "get-pointer",  which indicates where the next byte from which data will be read.

The ifstream method long tellg() returns the current position of the get pointer.

The ofstream method seekg(int n) sets the get offset to n bytes past the start of the stream.
The ofstream method seekg(int n, ios::beg) set the get offset to n bytes past the start of the stream.
The ofstream method seekg(int n, ios::cur) sets the get offset to n bytes past the current put offset.
The ofstream method seekg(int n, ios::end) sets the get offset to n bytes before the end of file.

 

/* This program demonstrates reading in student objects that
have been stored as constant size binary records both sequentially
and in a random access manner
*/


#include <iostream>
#include <fstream>

using namespace std;

#include "student.h"

void main(){

    /*
    read the contents of a file whose data is expected to be constant
    size Student records
    */


    char * fileName = "studentRecords.txt";

    ifstream file(fileName, ios::in); //ios::in -> input

    if(!file){
        cout << "ERROR: could not open file " << fileName << " ...exiting\n";
        exit(1);
    }


    Student student;

    //read students sequentially and print the ones with
    //valid student numbers along with their record location


    cout << "contents of file: " << fileName << "\n";
    cout << "=================================\n";

    int numberOfRecords = 0;
    int i=0;
    while(file && !file.eof()){

        file.read( (char *) &student, sizeof(student) );

        //if statement is to ensure that last student is not printed twice
        if(!file.eof()) {
            numberOfRecords++;
            if(student.getStudentNumber() > 0){
                cout << "[" << numberOfRecords-1 << "] " << student << "\n";
            }
        }
    }

    cout << "records read: " << numberOfRecords << "\n";

    cout<< "Please enter record number[0..." << numberOfRecords << "] (crtl-z to quit) ";

    int recordIndex;

    //read data from user via cin and write it to the text file
    while(cin >> recordIndex){
        Student aStudent;

        if(recordIndex >= 0 && recordIndex < numberOfRecords){

            //clear the EOF indicator and position the file get-pointer to the correct record
            file.clear();
            file.seekg(recordIndex * sizeof(aStudent));

            //read the student record
            file.read( (char *) &aStudent, sizeof(aStudent) );

            cout << aStudent << "\n";

        }
        cout<< "Please enter record number[0..." << numberOfRecords << "] (crtl-z to quit) ";

    }


    cout << "...closing input file\n";
    file.close(); //fstream destructor will also close the file

}