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?
[C++] File Handler Class
Started by CoryG89, Jul 27 2010 04:41 AM
8 replies to this topic
#1
Posted 27 July 2010 - 04:41 AM
#2
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
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
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
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
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.
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
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.
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
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?
FileHandler bookfile = new Filehandler(name of file);
or is that java?
Quote
I think they're the same.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.
-
Currently working on: the 3D engine for Tomb Raider.
#7
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
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
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.
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











