I'm having trouble with where to begin with an assignment. I'm hoping someone here will have a little more knowledge and be able to explain it to me. Just a point in the right direction please!! Here's the assignment:



Create a Class that reads a file completely into a
C-style string in memory. Try to do this with one call
to the iostream read routine.
Make sure your class frees up its resources in the destructor.
HINT: Use the ios::binary option when you open the file.


Your basic class should look something like:

class FileString
{
public:
  // If File errors exist place them into the string buffer 
  // which starts with **ERR: 
  // For example, **ERR: Couldn't open file
   FileString (string filename);

   // A call to value returns the contents of the file. 
   const char * value();
};

Recommended Answers

All 17 Replies

What he is saying is that he wants you to read the file all at once instead of line by line or int by int or whatever.

To do that, you have to know how big the file is in bytes in order to allocate the memory to store it (which is why you need to open it in binary mode, since that turns off special text file translations such as "\r\n" to "\n").

There is no function in standard C++ to tell you the length of a file. However, there are functions you can use together to get that information. Here's a handy reference. Try to find functions that will help you.

Then you can just use ifstream.read() to load the whole file into your newly-allocated character buffer.

Hope this helps.

OK so I have this to work with that I created earlier, but I'm not sure how to properly implement it so that it works with the class we were given..Any pointers?

int main()
   {
   for(int j=0; j<MAX; j++)            //fill buffer with data
      buff[j] = j;                     //(0, 1, 2, ...)
                                       //create output stream
   ofstream os("edata.dat", ios::binary);
                                       //write to it
  
   os.write( reinterpret_cast<char*>(buff), MAX*sizeof(int) );
 // Alternative form that I prefer
 // os.write( (char *)buff, MAX*sizeof(int));
   os.close();                         //must close it
   // Otherwise the file wouldn't be closed until the destructor executes

   for(int j=0; j<MAX; j++)                //erase buffer
      buff[j] = 0;
                                       //create input stream
   ifstream is("edata.dat", ios::binary);
                                       //read from it
   is.read( reinterpret_cast<char*>(buff), MAX*sizeof(int) );
   // Alternative form that I prefer
   // os.read ( (char *)buff, MAX*sizeof(int));
   cout << is.gcount() << " bytes read " << endl;

   for(int j=0; j<MAX; j++)                //check data
      if( buff[j] != j )
         { cerr << "Data is incorrect\n"; return 1; }
   cout << "Data is correct\n";
   return 0;
   }

You have not done what I have asked you. I don't know how you got that last code, but it is not going to help you at all. It doesn't do anything that relates to your original question.

Get the length of a file (in bytes) thus:

istream f( "foo.txt", ios::binary );
f.seekg( 0, ios::end );
unsigned long length = f.tellg();

You can then allocate space to read the file:

char *text = new char[ length +1 ];  // don't forget the null-terminator

And you can read it all at once:

f.seekg( 0 );
f.read( text );
text[ length ] = '\0';  // don't forget to terminate the string

Both seekg() and tellg() are listed in the (short) list at the link I gave you, and are the only methods that tell you where in the file you are.

Ok, so what can I use as my default constructor? I thought this would work, but it doesn't seem to be appropriate....:

class FileString
{
public:
  // If File errors exist place them into the string buffer 
  // which starts with **ERR: 
  // For example, **ERR: Couldn't open file
	FileString (string filename)
	{
[B]		fstream fs;

		fs.open ("newfile.dat", ios::binary);

		fs.close();[/B]
	};

I have no clue why that code shouldn't work. However, you should be opening filename instead of "newfile.dat".

You have no string buffer defined in your class. If you want to use it, you must first define it. Then you can test for error like you want:

class FileString {
  private:
    string buffer;
  public:
    FileString( string filename ) {
      fstream fs( filename, ios::binary );
      if (!fs) {
        buffer = "**ERR: could not open file";
        return;
        }
      // otherwise, read the file
      fs.close();
      }
  };

Good luck.

The exact error the Visual Studio compiler is giving me is this:

"cannot convert parameter 1 from 'std::string' to 'const char *'"

????

Sorry. I always forget that the stupid fstream constructors don't take std::strings.
Say: fstream fs( filename.c_str(), ios::binary ); instead.

You could have looked this up yourself.

Ok that works better. Can you explain what that is? Is that kind of like static casting it to a c-string or something??

Also, I'm still having trouble getting it to work...

My const char * value function is supposed to call the contents of the file, but I'm still not understanding what file I'm supposed to be calling and I can't figure how to write value so that it will get those contents. I thought this was close, but I don't know what to return then...?

const char * value()
{
	FileString f;
	ifstream infile("edata.dat", ios::binary);

	infile.seekg(0, ios::end);
	int endposition = infile.tellg();
	int n = endposition / sizeof(FileString);
	cout << "\nThere are " << n << " strings? in the file";
	return ??;
}

A std::string is just a fancy way of handling a c-string. So yes, you are getting the c-string out of it.

There is no way for you to know how many lines there are in the file, not until you read every line in the file, that is.

I don't know why you are dividing the file size by your FileString class's size (which, for all intents and purposes, you should consider as a random number). The length of the file is endposition. (Remember how I got the length of the file in post #4?)

Lines in a file are separated by newlines. On Unix, that's '\n'. On Windows, that's "\r\n". On a Mac, that's '\r'. It doesn't look like your teacher is interested in individual lines in the file. He just wants the whole thing in one string.

Your FileString class should have a method prototyped: const char *value(); which gets you the string (the whole file) that you read. (This is the same thing that std::string.c_str() did.)

You should be able to use your class like this:

int main()
{
  FileString f( "fooey.txt" );
  cout << "The file's contents are:\n"
       << f.value();
  return EXIT_SUCCESS;
}

Hope this helps.

Ok, good to know. Thank you. Now I'm here:

class FileString
{
private:
	string buffer;
public:
	FileString( string filename = "Okay")
	{
		fstream fs(filename.c_str(), ios::binary);
		if (!fs) 
		{
			buffer = "**ERR: could not open file";
			return;
		}
		  // otherwise, read the file
		fs.close();
	}

   const char * value();
};



const char * value()
{
	FileString f;
	ifstream infile("edata.dat", ios::binary);

	infile.seekg(0, ios::end);
	int endposition = infile.tellg();

	char *text = new char[endposition +1];

	infile.seekg(0);
	[B]infile.read(text);[/B]
	text[endposition] = '\0';
	
	return text;
}


int main()
{
	FileString f("fooey.txt");
	cout << "The file's contents are:\n" << f.value() << endl;
	return 0;
}

It's telling me now that 'read' can't take any parameters...Is there a different way I can do that?!?

Thanks

You need to think a little bit more about what you are doing. The file you are supposed to be reading is "fooey.txt" --specified when you created the FileString class. It doesn't have to be "fooey.txt" --it could be "edata.dat" or "myprog.cpp" or whatever, but you are supposed to tell it the filename when you create the class in main.

Also, why are you reading from a file in the value() method? (Watch your syntax. Since you defined value() outside the class { and }, you must prefix the name with the class name.)
You should not be opening or reading any files in value(). Remember what was said about c_str(). Also, what is 'buffer' for? Think about these things.

Lastly, your red line was my fault. istream.read() takes exactly two arguments and I used only one. It should say fs.read( text, endposition ); Sorry about that.

You should find a convenient reference you can use to look stuff up when you find errors. I like cppreference.com, but it is rather simplistic. There are others. Find one you like and use it religiously.

Get some sleep. ;)

Let me correct my last post. It says that the 'read' does not take 1 parameter. I assume that means I need something like infile.read(text, ios::binary);

That gets me a linking external error though, so I assume there is something else wrong with my value function. Anybody know what?!?

Thanks

Based on your comment of "Go to bed ;)" I'm guessing you're doing that shortly yourself, and unfortunately, this is not an option for me :(. lol This assignment has to be submitted by midnight tonight, which means I'll probably be struggling with it right up until 11:59! Anyway, if you're still there, or if anyone else can help...I think I've got it working but I don't know what I need to do so my buffer works with a proper error possibility. I don't completely understand what's going on there, so I don't know what to do with it. Sorry if I'm frustrating you or anyone, but like they say...."you can never ask too many questions!" Please and thank you!

#include <fstream>
#include <iostream>
using namespace std;


class FileString
{
private:
	string buffer;
public:
	FileString( string filename)
	{
		fstream fs(filename.c_str(), ios::binary);
		if (!fs) 
		{
			buffer = "**ERR: could not open file";
			return;
		}
		fs.close();
	}

   const char * value();
};


const char * FileString::value()
{
	ifstream infile;

	infile.seekg(0, ios::end);
	int endposition = infile.tellg();

	char * text = new char[endposition +1];

	infile.seekg(0);
	infile.read(text, endposition);
	text[endposition] = '\0';
	
	return text;
}


int main()
{
	FileString f("edata.dat");
	cout << "The file's contents are:\n" << f.value() << endl;
	return 0;
}

Let me correct my last post. It says that the 'read' does not take 1 parameter. I assume that means I need something like infile.read(text, ios::binary);

This is why we ask you to paste the exact error message rather than post your interpretation of the message :icon_wink:

Since I just got here, I have no idea what you now need to do. Asking a specific question when you post the code is helpful.

Insomnia, alas.

It's alright, WaltP, I've been following his train of thought. He's confused about the functions of each method in his class and how the class relates to the file.

Since your assignment was due some 4 1/2 hours past, here's a more explicit answer. You need to think about why it works:

file vs string
A file is just a giant collection of bytes on disk, like an array. It can be interpreted all kinds of ways. One way is as a text file.
Here's a string (in C/C++): Now is the time for all good men\nto come to the aid of\ntheir country. You can see that the string would print on three different lines, as

Now is the time for all good men
to come to the aid of
their country.

So, what is special about one line or the other? Nothing, only that each line has a '\n' character between itself and any neighbors.

The very same holds true for text files. Both are essentially an array of characters (what we like to call a string in code), with '\n' peppered in for good measure.


the assignment
Your teacher specifically stated he wanted you to read the entire file all at once. That is, don't bother reading it line by line or character by character. Just get the whole string. Notice how we are no longer thinking about the text file in terms of lines, but in terms of one giant string.

When you create your class the constructor is given the name of a file to read. The file should be opened, read into a string in the class, and closed. The class should not play with any other file, nor should it care what the name of the file actually is.

The file is read into a variable named buffer; it was declared in the private section of your class.

Then you are asked for access to the string data using the value() method. All that is needed is to return buffer.c_str(); Both c_str() and value() return const char *, so they are a perfect match. The type of a thing matters tremendously in programming. You can learn a lot about how things work and fit together by examining their types.


Well, that's it. You should be able to get somewhere between a D and a C with what you did. Don't be too discouraged. Just pay attention and keep learning how to think about things and it'll get better and easier.

Aarrrg..Ok well thanks for all your help. We have a lab due Saturday, that I need help with now also, so I'll be postin a new thread for that up real soon I'm sure :) Thanks again!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.