File I/O and Me!

Started by
4 comments, last by Zahlman 19 years, 7 months ago
Couple questions. I have a file with a list of multi-digit numbers, comments, etc. I am unsure of how many numbers will be in the file, since it can be edited by the user and I don't want to make them count them up. A file might look like this: ; filename.txt 3 71 33 45 113 2 I want to read these values and store them in an array. My current code works like this:

	std::ifstream InFile( DefFile );

	// Make sure the file is open.
	if ( !InFile.is_open() )
		throw "Input File Not Found!";


	ULONG Count = 0;	// Counts the number of elements in the list.
	int Ent = 0;		// Reads the input.
	char Test;			// Tests the input.

	// Count the number of items in the list.
	while ( !InFile.eof() )
	{
		// Read in a symbol.
		Test = InFile.peek();
		
		// Don't parse the lines starting with these symbols.
		if ( Test != ';' && Test != '\n' && Test != ' ' )
		{
			// Entry reached! Perform counting.
			while ( Ent != '\n' && !InFile.eof() )
			{
				Count++;
				InFile >> Ent;
			} // End while.
		} // End if.

		// Eat the newline character.
		InFile.ignore( 999, '\n' );
	} // End while.

	printf( "Count = %d", Count);

	// Allocate the needed space.
	pList = AllocateMemory( Count );
	
	//Go back to the beginning of the file.
	InFile.seekg(0, std::ios_base::beg);

	// Read in the actual values.
	while ( !InFile.eof() )
	{
		printf("bah");
		// Read in a symbol.
		Test = InFile.peek();
		
		// Don't parse the lines starting with these symbols.
		if ( Test != ';' && Test != '\n' && Test != ' ' )
		{
			// Entry reached! Perform counting.
			while ( Ent != '\n' && !InFile.eof() )
			{
				InFile >> Ent;
				// Store entries here.
			} // End while.
		} // End if.

		// Eat the newline character.
		InFile.ignore( 999, '\n' );
	} // End while.

	// Clean up.
	InFile.close();

Pretty simple. And this works (It prints the correct Count value). This code goes through and counts the number of entries in the file. Then I allocate the necessary memory (size of Count), then I have a code block essentially the same as above where I go through all the entries in the file again, and store them in the newly made array. Questions: 1. This seems inefficient. Is there a better way to do this instead of having to go through the file twice? 2. If this way is suitable... After the above code block, the file pointer is at the end of the file. The second code block will not work, unless that pointer is set back to the beginning of the file. How do I do this? I've tried InFile.seekg(ios::beg), InFile.seekg( 0 ), etc.. none seem to work. Thanks much. File I/O and I don't get along very well. ;) *edited to include full source. [Edited by - Doggan on September 25, 2004 9:49:26 PM]
Advertisement
Have you tried seekg(0, ios_base::beg)?
If that doesn't work, off the top of my head, a bad, but effective, way to reset the file pointer is to close it and then open it again. Since you are using ifstream (that is what you are using, right? FYI, you should include the declaration and initialization of your variables when you post code), you might have to use a separate ifstream object to do it that way, but try using ifstream::open() and ifstream::close() first.
This is where you can use vectors or linked lists.

Eg
#include <vector>#include <fstream>//This vector will store our numbers, it can grow and shrink automatically.std::vector<int> numberList;int temporary;int main(){ifstream fin;fin.open("myfile.txt");while(!fin.eof()){fin>>temporary;numberList.push_back(temporary);}//close the filefin.close();//Printing out of numbersfor(int i=0; i<numberList.size();++i){cout<<numberList<<" ";}return 0;}


I havent tested the source above, but yeah it looks something like this. In this case Linked List would have been more efficient, but i guess for such a small app, it doesnt make much of a difference.
Ok, the variables are there now :)

I tried the seekg(0, ios_base::beg) thing, and it doesn't work. The correct value of Count gets printed out. When I try to print out "bah", that never gets printed out.

Oddly, even when I close the file and re-open it between the two main while loops, the "bah" never prints.
Thanks GamerSg. That's a much more efficient way to do it. I'll go with that idea. :)
while ( Ent != '\n' && !InFile.eof() )

This is a serious bug.

(1) The code will not actually stop reading in Ent values at a newline, because Ent is an integer; therefore the newlines in the file will just be treated as whitespace between integers. That means the following will break:

; some comment
1 2 3 4 5
; Since the newline was just whitespace, now we're trying to read the semicolon in as an integer. Oops!

(2) The code *will* stop reading in Ent values if one of them is 13 (the ASCII value of '\n').

As for your question... why not just use a container that will resize itself appropriately (probably either std::vector or std::list)?

Probably a better way to design things is to skip lines starting with a semicolon (allow the ones starting with space; maybe the user will want to use leading spaces to line some numbers up), and otherwise read all numbers on the line:

// Count the number of items in the list.while ( !InFile.eof() ) {  // Read in a symbol.  if (InFile.peek() != ';') {    // Not a comment line.    while ( !InFile.eof() && InFile.peek() != '\n') {      // Until we hit the end of line,      InFile >> Ent;      // read in integer values.      pList.push_back(Ent); // assuming a std::vector      // You're still screwed if there is garbage embedded      // in the line; may want to check for valid data    }  } // otherwise, ignore the line.  // Eat the remainder of the line.  InFile.ignore( 999, '\n' );}// Now you don't have to do any repeating.


Once you get "checking for garbage" working, you could even shift it so that integers are read from each line up until the first bit of garbage is detected; that would allow using any marker (other than a number or whitespace) for a comment, and allow comments at the end of a line of numbers instead of just whole-line comments. And on top of that, it actually will simplify the code:

// Count the number of items in the list.while ( !InFile.eof() ) {  // Continually try to read integers and store them;  if (InFile >> Ent) {    pList.push_back(Ent);  } else {    // and any time you hit garbage (including right at the    // beginning of a line), skip to the next newline.    InFile.clear();    InFile.ignore(999, '\n');    // You should probably be using a better magic number    // than that, BTW ;) Look into std::numeric_limits.    // the .ignore() will handle the end-of-file gracefully,    // AFAIK.  }}

This topic is closed to new replies.

Advertisement