Jump to content


[C++] File Handler Class


8 replies to this topic

#1 CoryG89

    New Member

  • Members
  • Pip
  • 3 posts

Posted 27 July 2010 - 04:41 AM

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?

#2 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 27 July 2010 - 08:13 AM

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
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)

#3 CoryG89

    New Member

  • Members
  • Pip
  • 3 posts

Posted 28 July 2010 - 10:00 PM

Wernaeh said:

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...

#4 fireside

    Senior Member

  • Members
  • PipPipPipPip
  • 1588 posts

Posted 29 July 2010 - 05:58 AM

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.
Currently using Blender and Unity.

#5 CoryG89

    New Member

  • Members
  • Pip
  • 3 posts

Posted 29 July 2010 - 05:49 PM

fireside said:

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.

#6 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 29 July 2010 - 10:18 PM

fireside said:

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

Quote

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.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#7 fireside

    Senior Member

  • Members
  • PipPipPipPip
  • 1588 posts

Posted 29 July 2010 - 10:38 PM

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);
Currently using Blender and Unity.

#8 poita

    Senior Member

  • Members
  • PipPipPipPip
  • 322 posts

Posted 31 July 2010 - 02:09 PM

fireside said:

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.

#9 fireside

    Senior Member

  • Members
  • PipPipPipPip
  • 1588 posts

Posted 31 July 2010 - 04:42 PM

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.
Currently using Blender and Unity.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users