C++

How to Use C++ fstream

The fstream term stands for File Stream. Stream refers to a sequence of characters moving from the disk to the C++ program or from the C+ program to the disk. Moving characters from a file in disk to the program is inputting. Moving characters from the program to a file in the disk is outputting. Input-file-stream abbreviated as ifstream is structured by the template class, basic_ifstream. Output-file-stream abbreviated, ofstream is structured by the template class, basic_ofstream.

It is possible for inputting and outputting to take place in one session. This is made possible by the class template, basic_fstream. Now, fstream is a synonym for basic_fstream. fstream, which is still basic_fstream, uses basic_ifstream and ofstream to operate.

In order to do input alone, do output alone, or both in one session, it suffices to start the C++ program with the following (including stream):

    #include <fstream>
    #include <iostream>

This tutorial has four main sections: opening and closing of a file stream, output file stream, appending, input file stream, and editing a file. Editing a file means inputting and outputting a stream.

Article Content

Opening and Closing a File Stream

Before a stream can be opened, a stream object has to be created. Opening a stream means establishing a channel between the C++ program and the file in disk. This is accomplished through which the sequence of characters will move to the file; or through which sequence of characters will leave the file and come to the program; or through which characters will move to-and-fro.

A stream is opened only for writing (output), reading (input), or both reading and writing. It can also be opened for other reasons.

Before opening a stream, the stream object has to be constructed. The simplest way to express it is as follows in the C++ main() function:

    fstream strm;

Now, with the strm object, the fstream member functions, open() and close() can be used, preceding each with the dot operator. The following statement can be used to open an fstream for reading:

    void open("path/to/and/the/file", ios_base::in);

The open() member function returns void.

With the stream object, the statement would be:

    strm.open("path/to/and/the/file", ios_base::in);

Since the open() member function returns void, to know if the file in the disk was successfully opened, use the member function:

    bool is_open() const;

It returns zero for false if the file did not open and 1 for true if the file opened.

To open a file for writing, use:

    strm.open("path/to/and/the/file", ios_base::out);

“ios_base::in” means open for reading and “ios_base::out” means open for writing. To open a file for reading and writing, use:

    strm.open("path/to/and/the/file", ios_base::in | ios_base::out);

Note: the presence of “ios_base::in | ios_base::out”, here.

Closing a stream means closing the channel through which data can be sent to and fro between the program and the file. No more datum can be sent in either direction using that channel. Closing the stream is not closing the stream object. The same stream can still be used to open a new channel, which should be closed after use in transmission of data. Make it a habit of closing any file stream, after it has been opened. When a stream is closed, any data in memory that was supposed to have been in the file is sent to the file before actually closing. The member function prototype to close fstream is:

    void close();

It returns void, unfortunately. So, to know whether the closing was successful, use the member function:

    bool is_open() const;

If the closing were successful, this would return zero, meaning the stream is no longer open. If the closing were unsuccessful, it would return 1 and meaning the stream could not be closed.

Output File Stream Operation

Opening a File and Giving It a New Content
To open an output stream with fsream, just use “ios_base::out” alone in the open() member function. The following program opens a file, and it sends the content of a string to it:

    #include <fstream>
    #include <iostream>
    using namespace std;
   
    int main()
    {
        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::out);
        if (strm.is_open()) {
            char str[] = "A: This is the first line.\n"
                "B: This is the second line.\n"
                "C: This is the third line.\n";

            strm << str;
   
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }
        else
            cout<<"File could not be opened!"<<endl;

        return 0;
    }

The name of the file is doc1.txt in the directory, dir1 in the user home directory. The directory, dir1, should already exist. If doc1.txt did not already exist, it would be created. If it existed and had any content, the content would be replaced.

The new content is identified by str in the program. At the end of the program, the string content would have been inserted into the stream and thus, the file with the statement:

    strm << str;

Cout is a standard output object, and it is typically used for the console. It uses the extraction operator, << . The extraction operator is also used with file streams. The file stream object here is strm.

The ‘\n’ character at the end of each quotation above is to ensure the next line appears below in the output file:

basic_ostream<charT, traits>& write(const char_type* s, streamsize n)

Instead of sending text to the file with the insertion operator, the write() member function can be used.

The following code illustrates this:

        fstream strm;
        strm.open("dir1/temp.txt", ios_base::out);
        if (strm.is_open()) {
            char str[50] = "Here we are";
            strm.write(str, 11);

            strm.close();
            if (strm.is_open())
                cout << "Stream could not close for writing!" << endl;
        }

The first argument of the write() function is the identifier of the character array. The second argument is the number of characters (without \0) in the array.

Appending Characters to a File

To append text to a file, use “ios_base::app” alone, instead of “ios_base::out” in the open() member function. Still, use the insertion operator, <<, as follows:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::app);
        if (strm.is_open()) {
            char str[] = "D: This is the fourth line.\n";

            strm << str;
   
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

The output file should now have four lines.

Input File Stream Operation

Reading Whole File Character by Character
To read a file with fstream, use “ios_base::in” alone, in the open() member function. The following program reads all the content of the file and displays it at the console:

    #include <fstream>
    #include <iostream>
    using namespace std;
   
    int main()
    {
        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char c;

            while (!strm.eof()) {
                strm.get(c);
                cout << c;
            }

            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

        return 0;
    }

The eof() is a member function, and it returns 1 when the end-of-file is reached and zero otherwise. The program reads the characters of the file, one by one, until the end-of-file is reached. It uses the get() member function, putting the read character into the variable c, which has already been declared. cout sends each character to the console.

The output should be:

A: This is the first line.
B: This is the second line.
C: This is the third line.
D: This is the fourth line.

Reading the Whole File With One Function
The whole file can be read using the member function:

basic_istream<charT, traits>& get(char_type* s, streamsize n, char_type delim);

It copies characters from the file and puts them into a character array. It does this until it meets the delimiter, EOF, or until it has copied the n – 1 character. It will fit the NUL (‘\0’) character as the last consecutive character in the array. This means the number of characters chosen for the array should be estimated to be at least the number of file characters (including any \n), plus one for the NUL character. It does not copy the delimiter character. The following code copies the whole file of doc1.txt, using this member function:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char arr[150];

            strm.get(arr, 150, EOF);
            cout << arr << endl;
           
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

The get() member function here is an overloaded member function of the get() function above.

Reading Line by Line
The member function to use here is:

basic_istream<charT, traits>& getline(char_type* s, streamsize n, char_type delim);

It copies characters from the file and puts them into a character array. It does this until it meets the delimiter (e.g. ‘\n’) or until it has copied the n – 1 character. It will fit the NUL (‘\0’) character as the last consecutive character in the array. This means the number of characters chosen for the array should be estimated to be at least the number of visible characters, plus one for the null character. It does not copy the delimiter character. The following code copies the whole file of doc1.txt line by line, using this member function:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char arr[100];

            while (!strm.eof()) {
                strm.getline(arr, 100, '\n');
                cout << arr << endl;
            }

            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

Since ‘\n’ is not copied when copying a line, endl has to be used for the output display. Note that the number of characters in the array and streamsize variable, have been made the same.

If it is known in advance that the delimiter is ‘\n’ then the following member function can be used:

basic_istream<charT, traits>& getline(char_type* s, streamsize n);

basic_istream<charT, traits>& seekg(pos_type pos)

Characters including ‘\n’ have their natural positions in the file, beginning from 0, then 1, 2, 3, and so on. The seekg(pos) member function would point the pointer to the character of a position in the stream object. Then, get(c) can be used to obtain that character.

The character in the 27th position of the current doc1.txt file is ‘B’. The following code reads and displays it:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char c;

            strm.seekg(27);
            strm.get(c);
            cout << c << endl;
           
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

If the position given is greater than that of the last character in the file (minus 1), null is returned.

pos_type tellg()

As a file is being read, an internal pointer points to the next character to be read. The tellg() member function can get the position number of the character the pointer is pointing to. When the file is just opened, tellg() will return 0 for the first character. After some reading, tellg() would return a number like 27 in the example above. The following code displays two position numbers and their corresponding characters, using the tellg() function:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char c;

            int no = strm.tellg();
            strm.seekg(no);
            strm.get(c);
            cout << no << ' ' << c << endl;

            no = 27;
            strm.seekg(27);
            strm.get(c);
            cout << no << ' ' << c << endl;
           
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;

The output is:

    0 A
    27 B

The equivalent function for outputting is tellp().

seekdir

seekdir means seek direction. Its constants defined in the ios_base library are: beg for the beginning of the file, cur for the current position of the file, and end for ending of the file. The above seekg() function is overloaded for the input stream as:

    basic_istream& seekg(off_type, ios_base::seekdir)

So, if the internal pointer is pointing to the character at position 27 by counting the beginning from 0, then

            strm.seekg(0, ios_base::cur);

Will maintain the pointer at the current position.

            strm.seekg(5, ios_base::cur);

Will take the pointer 5 places ahead to point at “i” in the second “This” of the doc1.txt file.

            strm.seekg(-5, ios_base::cur);

Will take the pointer 5 places behind to point at “i” in the first “line” of the doc1.txt file. Note that the position of the newline character ‘\n’, which is not displayed at the output, is counted.

Now, no matter where the pointer might be,

            strm.seekg(0, ios_base::beg);

Takes and maintains the pointer at the beginning of the file; to point to the first character of the file, with an offset of 0. In this case, it will point to “A”.

            strm.seekg(5, ios_base::beg);

Will take the pointer to the beginning with an offset of 5 places ahead; point at “i” in the first “This” of the doc1.txt file. Note that the single space is counted as one character.

A negative integer in the offset position for “ios_base::beg” is not useful.

Well, no matter where the pointer might be,

            strm.seekg(0, ios_base::end);

Will take and maintain the pointer just after the end of the file; to point to nothing.

A positive integer in the offset position for “ios_base::end” is not useful.

            strm.seekg(-5, ios_base::end);

Will take the pointer to the end with an offset of 5 places behind; point at “i” in the last “line” of the doc1.txt file. Note that ‘\n’ and the dot are counted as one character each.

The following code illustrates the use of the function, at the current position, with a negative and positive offset:

        fstream strm;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char c;

            strm.seekg(27);
            strm.seekg(0, ios_base::cur);
            strm.get(c);
            cout << c << endl;

            strm.seekg(-5, ios_base::cur);
            strm.get(c);
            cout << c << endl;

            strm.seekg(+10, ios_base::cur);
            strm.get(c);
            cout << c << endl;
           
            strm.close();
            if (strm.is_open())
                cout << "Stream could not close!" << endl;
        }

The output is:

    B
    n
    space

The get() member function shifts the pointer one place ahead after its execution.

The equivalent function for outputting is:

basic_ostream<charT, traits>& seekp(off_type, ios_base::seekdir)

Note the “p” in seekp for put, as opposed to “g” in seekg for get.

Editing a File

Classical File Editing in C++
To edit a file, the file should be opened for reading and writing, otherwise known as input and output. In the classical approach, characters are read one by one and changed one by one. All the characters of the file are read into a char array. The array is modified using the character positions that correspond to the positions in the file. After that, the array content is sent back to the file to replace the old content. The modification is typically done while the file is being read.

To replace a character, simply replace it in the array. To delete a character, bring down all the characters ahead in one place. To insert a character, shift all the characters ahead by one place and insert. In order to accomplish this, the size of the array should be estimated to be at least the number of all the final characters.

In order to carry out the following task, back up the file, doc1.txt in the same directory, renaming it to doc1Back.txt. In the following code sample, when a character is read, it is checked, before it is edited. In the code, “B: This”, which consists of 7 characters, in the second line of the doc1.txt file, is deleted:

        fstream strm;
        char arr[150];
        int ctr = 0;
        strm.open("dir1/doc1.txt", ios_base::in);
        if (strm.is_open()) {
            char c;

            int diff = 7;
            bool bl = true;
            while (!strm.eof()) {
                strm.get(c);  
                if (bl == true) {
                    if (c == 'B') {
                        bl = false;
                        diff = diff - 1;
                        if (diff == 0)
                            bl = true;
                    }
                    else {
                        arr[ctr] = c;    
                        ctr = ctr + 1;
                    }
                }
                else if (diff > 0) {
                    diff = diff - 1;
                    if (diff == 0)
                        bl = true;
                }
            }

            strm.close();
            if (strm.is_open())
                cout << "Stream could not close for reading!" << endl;
        }

        strm.open("dir1/doc1.txt", ios_base::out);
        if (strm.is_open()) {

            strm.write(arr, ctr-1);

            strm.close();
            if (strm.is_open())
                cout << "Stream could not close for writing!" << endl;
        }

The new file presentation is:

A: This is the first line.
is the second line.
C: This is the third line.
D: This is the fourth line.

The following code segment is typed twice in the above code:

                        if (diff == 0)
                            bl = true;

In order to replace “B: This”, which consists of 7 characters, in the second line of the doc1.txt file, with “2: Now, here” of 12 characters, this code should be replaced with:

                    if (diff == 0) {
                        bl = true;
                        for (int i=0; i<12; i++) {
                            arr[ctr] = repl[i];
                            ctr = ctr + 1;
                        }
                    }

where repl[] is,

        char repl[] = "2: Now, here";

The code should be typed in two places. The output will be:

A: This is the first line.
2: Now, here is the second line.
C: This is the third line.
D: This is the fourth line.

Conclusion

The fstream class deals with input from a file to a C++ program and output from the program to the file. In order to use the C++ fstream, an object from the class has to be instantiated. The stream object then has to be opened for input or output or both. To append text to the file, the stream has to be opened for appending. Make a habit of always closing the stream after it has been opened and used. If the file is an image file, then “ios_base::binary” will have to be ORed using | , with the second argument of the open() member function. This article hopefully assisted you in using the C++ fstream.

About the author

Chrysanthus Forcha

Discoverer of mathematics Integration from First Principles and related series. Master’s Degree in Technical Education, specializing in Electronics and Computer Software. BSc Electronics. I also have knowledge and experience at the Master’s level in Computing and Telecommunications. Out of 20,000 writers, I was the 37th best writer at devarticles.com. I have been working in these fields for more than 10 years.