[C++] File Handler Class

18f445d3d2078ae56de4dd0f7b8420ae
0
CoryG89 101 Jul 27, 2010 at 04:41

I am trying to design a class that will handle all of my general file operations for the application I am working on currently, as well as any I might do in the future in C++. The class is built so that I have a public fstream object as a member. The class seems to be working fine, all except for that it will not seem to read the data from the file when I go through the object and use the file stream as a public member. I am doing everything exactly the same as I would normally so I don’t understand why this is happening.

I remember reading somewhere that when you pass a file stream to a function, you must do it by reference. For what reason is this? Perhaps it has something to do with why I cannot get my stream to read data from my file when it is part of an object? Anyone have any ideas?

8 Replies

Please log in or register to post a reply.

B7568a7d781a2ebebe3fa176215ae667
0
Wernaeh 101 Jul 27, 2010 at 08:13

Suggestions for the error reason are hard to give without any code excerpt. Double check your file names, check the stream error states after each operation, …

The following

class MyFile
{
public: std::ifstream myStream;
};

MyFile file;
file.myStream.open("SomeFile.txt");
char buffer[512];
file.myStream.getline(buffer, 512);
file.myStream.close();

should work as expected.

Concerning the reference issues, you shouldn’t actually be able to pass a file stream around by value (there is no copy constructor), so pass-by-reference and pass-by-pointer are the only available options…

Apart from that - why do you write a wrapper around fstream ? The only reason I’d see for something like this is the ability to switch implementation to compressed resource files or network files on the fly. Then, however, the boost IO Iosteams libraries (boost::iostreams::device, …) provide a better access to the entire concept than the STL.

Cheers,
- Wernaeh

18f445d3d2078ae56de4dd0f7b8420ae
0
CoryG89 101 Jul 28, 2010 at 22:00

@Wernaeh

Apart from that - why do you write a wrapper around fstream?

Well, it was mostly for practice implementing classes and exception handling. I am currently working on a program for class that can be used to run a book store. So really the only thing the class is acting as is a wrapper, with extended exception handling and a few useful functions I plan on writing that specifically work on data files. For instance I have a function that counts the number of lines in a data file, another that will count the number of datum on each line using the number of delimiters. The class is also responsible for storing the delimiter used, also a bool flag stating whether a file is currently open or not.

#include <cstring>
#include <cstdlib>
#include <fstream>
#include <string>
#include <cctype>

using namespace std;

enum FileModes { APPEND, INSERT, INPUT, OUTPUT, TRUNCATE };

//////// NODE COMPOSITION - self referential data structure
class FileHandler
{
    private:
        char filename[51];              // filename with a maximum of 50 characters
        int fileMode;                   // determines how the data file is opened for use is the stream

    public:
        fstream stream;                 // C++ stream capable of reading, writing, or both simultaneously
        bool status;                    // true if the file is in use
        char delimiter;                 // stores the delimiter used

        // Exception classes
        class DoesNotExist { };
        class NoFileName { };
        class CannotOpen { };

        //////// CONSTRUCTOR / DESCTRUCTOR
        FileHandler(char *newFile)
        {
            strcpy_s(filename, newFile);    // set the filename for our filehandler
            status = false;                 // file has not been opened yet
        }
        ~FileHandler()
        {

        }

        //////// METHODS

        // stores a new delimiter for the stream
        void setDelimiter(char newDel)
        {
            delimiter = newDel;
        }

        // returns the number of lines in the stream
        int CountLines(bool countBlanks = false)
        {
            int count = 0;
            string buffer;

            while(getline(stream,buffer))
            {
                if(buffer != "")      // only count if line isn't blank
                    count++;
                else if(countBlanks == true)    // line is blank, only count if true
                    count++;
            }

            return count;
        }

        // returns number of individual datum on the first line; others are assumed to be the same
        int CountCells()
        {
            int numOfCells = 0;
            char buffer = ' ';
            int testCount = 0;

            while(buffer != '\n')
            {
                stream.get(buffer);     // read a single character into the buffer
                if(buffer == delimiter)
                    numOfCells++;
                cout << "Contents of Buffer: " << buffer << endl;
                cout << "Number of Cells: " << numOfCells << endl;
                system("pause");
            }

            numOfCells++;

            cout << "Final number of cells: " << numOfCells << endl;
            system("pause");

            return numOfCells;
        }

        // Verifies that there is a stored filename in the FileNode, if not it obtains one
        void verifyFilename()
        {
            try             
            {
                if(!filename)
                    throw NoFileName();
            }
            catch(NoFileName)
            {               
                cout << "There is no valid filename loaded." << endl;
                cout << "Please enter the filename of the desired stream: ";
                cin >> *filename;
            }
        }

        // Opens a file for both input and output
        void openFile()
        {
            try
            {
                stream.open(filename, ios::in | ios::out | ios::app);
                if(stream.fail())
                    throw CannotOpen();
                else
                    status = true;  // set flag open
            }
            catch(CannotOpen())
            {
                cout << "ERROR: The file cannot be opened." << endl;
                system("pause");
            }
        }

        // Opens a file using a specified mode, see global enum for specifics on the modes
        void OpenFile(int mode)
        {   
            verifyFilename();
        
            switch(mode)
            {
                case 0:     // "APPEND" contents preserved | all output at end of file
                    try
                    {
                        
                        stream.open(filename, ios::out | ios::app);
                        if(stream.fail())
                            throw CannotOpen();
                        else
                            status = true;  // set flag open
                    }
                    catch(CannotOpen())
                    {
                        cout << "ERROR: The file cannot be opened." << endl;
                        system("pause");
                    }
                    break;
                case 1:     // "INSERT" contents preserved | output written anywhere
                    try
                    {
                        stream.open(filename, ios::out | ios::ate);
                        if(stream.fail())
                            throw DoesNotExist();
                        else
                            status = true;  // set flag open
                    }
                    catch(CannotOpen())
                    {
                        cout << "ERROR: The file cannot be opened." << endl;
                        system("pause");
                    }
                    break;
                case 2:     // "INPUT" if file does not exist, it will not be created
                    try
                    {
                        cout << "Open Mode: INPUT" << endl;
                        system("pause");
                        stream.open(filename, ios::in);
                        if(stream.fail())
                            throw DoesNotExist();
                        else
                            status = true;  // set flag open
                    }
                    catch(DoesNotExist)
                    {
                        cout << "ERROR: The file does not exist." << endl;
                        system("pause");
                    }
                    break;
                case 3:     // "OUTPUT" the file's contents will be deleted if it exists
                    try
                    {
                        stream.open(filename, ios::in);
                        if(stream.fail())
                            throw CannotOpen();
                        else
                            status = true;  // set flag open
                    }
                    catch(CannotOpen)
                    {
                        cout << "ERROR: The file cannot be opened." << endl;
                        system("pause");
                    }
                    break;
                case 4:     // "TRUNCATE" opened for input & output, contents deleted
                    try
                    {
                        stream.open(filename, ios::in | ios::out | ios::trunc);
                        if(stream.fail())
                            throw CannotOpen();
                        else
                            status = true;  // set flag open
                    }
                    catch(CannotOpen)
                    {
                        cout << "ERROR: The file cannot be opened." << endl;
                        system("pause");
                    }
                    break;
            }
        }

        // This is mostly a wrapper function, will simply call the filestream's close() function, but will
        // also set our status flag to back to false
        void CloseFile()
        {
            stream.close();
            status = false;
        }

};

So, here’s the thing. I am not actually getting any errors, the program run’s fine it is just that no data actually get’s read, all the variables that I read data into are empty. As you can see the opening and closing of the data file is handled in the object itself. But when I go to read data from the file I do it like the implementation you show.

FileHandler BookFile;
BookFile.OpenFile(INPUT);
BookFile.stream.getline(.....

I do it like this and the variables I read into end up holding nothing. But if I open up a seperate stream outside my object and read them into the same variables the exact same way but with a different file stream it works perfectly. Kind of baffling…

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 29, 2010 at 05:58

Not being a c++ guru or anything, but when you instantiate it’s normally:

FileHandler bookfile = new Filehandler(name of file);

or is that java? I think they’re the same. Anyway, you have the file name in the constructor but you haven’t passed it in your code you gave for an example unless that was an abbreviated code or something. I don’t know about passing a stream by reference, but you’re not doing that. You’re just passing the filename and the class is opening the stream and using it, so it’s essentially the same thing you would be doing otherwise.

18f445d3d2078ae56de4dd0f7b8420ae
0
CoryG89 101 Jul 29, 2010 at 17:49

@fireside

Not being a c++ guru or anything, but when you instantiate it’s normally:

FileHandler bookfile = new Filehandler(name of file);

I don’t know about passing a stream by reference, but you’re not doing that. You’re just passing the filename and the class is opening the stream and using it, so it’s essentially the same thing you would be doing otherwise.

Yeah that is how I have it instantiated. I just typed that up to fast right there. My bad. And yeah, I’m not passing a file stream by reference, the file stream is created when the object is instantiated and I access it through the dot operator. This just doesn’t seem to be working, and I remember reading that a file stream has to be passed by reference or pointer. I was just wondering if this may have anything to do with why it isn’t working. I guess not though.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Jul 29, 2010 at 22:18

@fireside

Not being a c++ guru or anything, but when you instantiate it’s normally:

FileHandler bookfile = new Filehandler(name of file);

or is that java?

Yes

I think they’re the same.

No. In C++, everything is a value type. If you want to have a reference rather than a value, you should use T* or T&. The new operator yields a pointer to the created object (T*). A definition like FileHandler bookfile; implicetly constructs bookfile.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 29, 2010 at 22:38

Thanks oisyn. I’d better brush up on my c++ by writing a few programs in it again. Apparently it should have been FileHandler bookfile(filename);

36b416ed76cbaff49c8f6b7511458883
0
poita 101 Jul 31, 2010 at 14:09

@fireside

Thanks oisyn. I’d better brush up on my c++ by writing a few programs in it again. Apparently it should have been FileHandler bookfile(filename);

They’re both right, they’re just used in different situations. In general, not using new is the preferred way, and you should only use new when you need to.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 31, 2010 at 16:42

I think my syntax was off, though. If it would have been for a pointer, I should have written

Filehandler *bookfile = new Filehandler(filename);

I’ve been in Java and As3 a lot lately, but I get you’re point. I think most people using C++ use pointers because they want a reference with a class. Anyway, my point was that the code shown wasn’t passing the filename to the constructor. I had actually forgotten about the other way because I hardly ever see anyone use it with a class so it looked weird and it doesn’t even exist in Java, I don’t think. There would be too much overhead with it. I think some people declare like that and then pass by reference, but it’s not many.