Hello, I'm new to programming forums so go easy on me :)

I'm working on an assignment for school.
I have to read a quiz.dat file which cotains info in the format of:

TTTTTFFFFFTTTTT
TTTTTFFFFF  TTTTJoe Jones
TTTTTFFFFFT  TTFMary Jane
...
... etc.

The first line being the answer key and subsequent being the persons answers followed by their names

I can read in key and initial answer and persons name but my loop to compare the arrays isnt working so well was wondering if i could get a hint

My problem i think is not being able to sort through it i have 3 int variables named correct, missing, wrong

thinking...

for(int i = 0; i < sizeof(answer); i++)
{
      if(answer[i] == key[i] && answer[i] != ' ')
            correct++
}

this is the first part wondering why something like this wouldnt work im getting a logic error its not counting my spaces, correct and wrong matches

anything helps and if you need to see more code ill post it im at work tryin to do this while boss is gone for a few mins.

-James

Edited 3 Years Ago by Reverend Jim: Fixed formatting

Using "sizeof" here seems problematic. I don't know where you are declaring answer, but remember that sizeof is not the number of elements in an array, but instead is the amount of storage required. I'd have to see how/where you declare everything, but I'm guessing you don't want "sizeof". A regular array size variable might be better:

const int NUM_QUESTIONS = 10;
int key[NUM_QUESTIONS];
int answer[NUM_QUESTIONS];

// initialize whatever you need to initialize and read in data

for(int i = 0; i < NUM_QUESTIONS; i++)
{
    if(answer[i] == key[i] && answer[i] != ' ')
        correct++
}

hey ill just put the code in here its not finished ill still have to add a function and code for an output object

this is the code without any loops im just doing it this way to try and learn how to pull lines from a dat or txt file

#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
using std::fixed;
using std::ios;
using std::left;
using std::right;

#include <iomanip>
using std::setprecision;
using std::setw;

#include <fstream>
using std::ifstream;

#include <string>
using std::string;

#include <cstdlib>
using std::exit;

int main()
{
    ifstream inQuiz("quiz.dat", ios::in);//create ifstream object

    if(!inQuiz)//if file did not open correctly
    {
        cerr <<"File could not be opened." <<endl;
        exit(1);
    }

    inQuiz.seekg(0);//set position to begining of file
    char key[16];
    char answer[16];
    char fstNm[15];

    inQuiz.getline(key, sizeof(key));//get answer key and store in key
    inQuiz.seekg(1 - 1, ios::cur);
    inQuiz.getline(answer, sizeof(answer));
    inQuiz.seekg(1 - (sizeof(answer)), ios::cur);

    cout << left << setw(15) << "Name" << setw(8) << "Correct"
         << right << setw(10) << "Missing" << setw(10) << "Wrong"
         << setw(10) << "Average" << endl << endl;


    int missing, correct, wrong, cnt;
    missing = correct = wrong = cnt = 0;
    double average;

    inQuiz.getline(fstNm, sizeof(fstNm));

        for(int i = 0; i < 16; i++)
            {
                if(answer[i] == 'T' && key[i] == 'T')
                    correct++;
            }

        for(int i = 0; i < 16; i++)
            {
                if(answer[i] == 'F' && key[i] == 'F')
                    correct++;
            }

    average = (14 / 15) * 100;

    cout << left << setw(9) << fstNm << right << setw(12) << fixed << correct
         << setw(11) << missing << setw(10) << wrong << setw(10) << setprecision(2)
         << average << endl;

    cout << answer << endl << key;

    inQuiz.close();

    return 0;
}

thanks for your help :) greatly appreciated

-James

Edited 3 Years Ago by Reverend Jim: Fixed formatting

>>inQuiz.seekg(0);//set position to begining of file

Delete that line. When the file is opened the file pointer will already be set to the beginning of the file.

>>inQuiz.seekg(1 - 1, ios::cur);
Why? what purpose does that serve?

#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
using std::fixed;
using std::ios;
using std::left;
using std::right;

#include <iomanip>
using std::setprecision;
using std::setw;

#include <fstream>
using std::ifstream;

#include <string>
using std::string;

#include <cstdlib>
using std::exit;

int main()
{
	ifstream inQuiz("quiz.dat", ios::in);//create ifstream object

	if(!inQuiz)//if file did not open correctly
	{
		cerr <<"File could not be opened." <<endl;
		exit(1);
	}
	
	inQuiz.seekg(0);//set position to begining of file
	char key[16];
	char answer[16];
	char fstNm[15];
	
	inQuiz.getline(key, sizeof(key));//get answer key and store in key
	inQuiz.seekg(1 - 1, ios::cur);
	inQuiz.getline(answer, sizeof(answer));
	inQuiz.seekg(1 - (sizeof(answer)), ios::cur);

	cout << left << setw(15) << "Name" << setw(8) << "Correct"
		 << right << setw(10) << "Missing" << setw(10) << "Wrong"
		 << setw(10) << "Average" << endl << endl;

	
	int missing, correct, wrong, cnt;
	missing = correct = wrong = cnt = 0;
	double average;
	
	inQuiz.getline(fstNm, sizeof(fstNm));

		for(int i = 0; i < 16; i++)
			{
				if(answer[i] == 'T' && key[i] == 'T')
					correct++;
			}

		for(int i = 0; i < 16; i++)
			{
				if(answer[i] == 'F' && key[i] == 'F')
					correct++;
			}

	average = (14 / 15) * 100;
		
	cout << left << setw(9) << fstNm << right << setw(12) << fixed << correct
		 << setw(11) << missing << setw(10) << wrong << setw(10) << setprecision(2)
		 << average << endl;
		
	cout << answer << endl << key;

	inQuiz.close();

	return 0;
}

You don't need to know whether answer is 'T' or 'F'. Just test if( answer[i] == key[i]) You can then delete one of those two loops.

You don't need to know whether answer is 'T' or 'F'. Just test if( answer[i] == key[i]) You can then delete one of those two loops.

I havent put the other code in for the other loops but i need to be able to count the amount that were correct wrong or missing

if(!inQuiz)//if file did not open correctly
	{
		cerr <<"File could not be opened." <<endl;
		exit(1);
	}

There is no reason to call a function to exit from main() . Just return 0;

inQuiz.seekg(0);//set position to begining of file

When you open a file you are at the beginning of the file. No need to seek.

inQuiz.seekg(1 - 1, ios::cur);
	inQuiz.getline(answer, sizeof(answer));
	inQuiz.seekg(1 - (sizeof(answer)), ios::cur);

Again, why do you have to seek? You read the answers and the name is read next. It's automatic. Seeking is only necessary when moving non-linearly through a file. You are making file reading way too complicated.

I havent put the other code in for the other loops but i need to be able to count the amount that were correct wrong or missing

Well then just do it in one loop. Try to use as few loops as possible because they are very time expensive.

if( answer[i] == ' ')
   missing++;
else if( answer[i] == key[i] )
   correct++;
else
   wrong++;

Edited 6 Years Ago by Ancient Dragon: n/a

Well then just do it in one loop. Try to use as few loops as possible because they are very time expensive.

if( answer[i] == ' ')
   missing++;
else if( answer[i] == key[i] )
   correct++;
else
   wrong++;

Thanks for the loop suggestion, I've dropped that into my code.
It compiles but when i run this code the output is all scattered

#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
using std::fixed;
using std::ios;
using std::left;
using std::right;

#include <iomanip>
using std::setprecision;
using std::setw;

#include <fstream>
using std::ifstream;

#include <string>
using std::string;

#include <cstdlib>
using std::exit;

int main()
{
	ifstream inQuiz("quiz.dat", ios::in);//create ifstream object

	if(!inQuiz)//if file did not open correctly
	{
		cerr <<"File could not be opened." <<endl;
		exit(1);
	}
	
	char key[16];
	char answer[16];
	char fstNm[15];

	inQuiz.getline(key, 16);//get answer key and store in key

	cout << left << setw(15) << "Name" << setw(8) << "Correct"
		 << right << setw(10) << "Missing" << setw(10) << "Wrong"
		 << setw(10) << "Average" << endl << endl;


	while(inQuiz >> answer >> fstNm)
	{
		
		int missing, correct, wrong;
		missing = correct = wrong = 0;
		double average;

		for(int i = 0; i < 16; i++)
		{
			if(answer[i] == ' ')
				missing++;
			else if(answer[i] == key[i])
				correct++;
			else
				wrong++;
		}

		average = (correct / 15) * 100;
			
		cout << left << setw(9) << fstNm << right << setw(12) << fixed << correct
			 << setw(11) << missing << setw(10) << wrong << setw(10) << setprecision(2)
			 << average << endl;
			
		cout << answer << endl << key;
	}

	inQuiz.close();

	return 0;
}

Line 61:

average = (correct / 15) * 100;

If correct is less than 15, you are going to get 0. Integer division truncates.

http://en.wikipedia.org/wiki/Division_%28mathematics%29#Division_of_integers

See point 4:

Give the integer quotient as the answer, so 26 / 10 = 2. This is sometimes called integer division.

Try multiplying, THEN dividing.

average = (correct * 100) / 15;

Regarding sizeof, it turns out here since you are dealing with characters and since characters take one byte, I think you were OK. But if it's any other type (i.e. integer) that doesn't take a byte per element, you are going to have potential problems.

Edited 6 Years Ago by VernonDozier: n/a

Line 61:

average = (correct / 15) * 100;

If correct is less than 15, you are going to get 0. Integer division truncates.

http://en.wikipedia.org/wiki/Division_%28mathematics%29#Division_of_integers

See point 4:

Try multiplying, THEN dividing.

average = (correct * 100) / 15;

Regarding sizeof, it turns out here since you are dealing with characters and since characters take one byte, I think you were OK. But if it's any other type (i.e. integer) that doesn't take a byte per element, you are going to have potential problems.

Thanks, I replaced the average calculation and that seems to be working just fine now :) as per the output im getting a run time error.

Debug Error!

Run-Time Check Failure #2 - Stack around the variable 'key' was corrupted.

I'm not sure if I'm reading in the file right
the file used for input is quiz.dat contents are as followed:
(top line of 'TTTTTFFFFFTTTTT' is the answer key)

TTTTTFFFFFTTTTT
TTTTTFFFFF TTTTJoe Jones
TTTTTFFFFFTTTTFJanet Jerome
TTTTTFFFFFTTTFFMary Smith
...(etc, 7 more lines with different names)

I thought I would have to use a seekg()/seekp() function to place the pointer to the next line after getting the key and then inputting the first answer and user name.

Time to do some debugging. You need to find out exactly what you are reading in. Look at your file and decide how many times you are supposed to go through the while loop.

Comment out lines 51 through 59. Compile and run the program again? Do you still get the error?

Now add these lines at the very top of your while loop:

cout << "Top of while loop\n";
cout << answer << "\t" <<  fstNm << "\n";
cout << "Press Enter to continue\n";
std::cin.get ();

Make sure that it displays what you think it is supposed to display. See exactly how far you get. Find out exactly when it stops working by sprinkling some more cout statements and pauses in there. You'll delete these later. Also, now might be a good time to learn how to use a debugger too. It's faster than sprinkling all of these cout statements in there, plus you don't have to remember to take them out.

You need to find out exactly where and when this error occurs.

Time to do some debugging. You need to find out exactly what you are reading in. Look at your file and decide how many times you are supposed to go through the while loop.

Comment out lines 51 through 59. Compile and run the program again? Do you still get the error?

Now add these lines at the very top of your while loop:

cout << "Top of while loop\n";
cout << answer << "\t" <<  fstNm << "\n";
cout << "Press Enter to continue\n";
std::cin.get ();

Make sure that it displays what you think it is supposed to display. See exactly how far you get. Find out exactly when it stops working by sprinkling some more cout statements and pauses in there. You'll delete these later. Also, now might be a good time to learn how to use a debugger too. It's faster than sprinkling all of these cout statements in there, plus you don't have to remember to take them out.

You need to find out exactly where and when this error occurs.

Thx for your time on this for me.

I've found that the while( inQuiz >> answer >> fstNm)

because the line i want to input has a space in it answer gets delimited by the space (TTTTTFFFFF TTTT)

any ideas on reading the array including the space cause i want it to specifically read in those 15 characters and appending the null on end

At this moment I need to figure out how to read in a char array including the white spaces cause cin get() and even getline() all delimit at the whitespace when it comes to the gap on the answer

is it possible to use a cast?? maybe to take it in like a string but store in a char array cause the ifstream wont like me input like a string

cin get() and even getline() all delimit at the whitespace when it comes to the gap on the answer

getline doesn't treat a space as a delimiter (unless you specifically tell it to). It delimits at '\n'. It "gets" a line, spaces and all (hence "getline"). Post your code.

At this moment I need to figure out how to read in a char array including the white spaces cause cin get() and even getline() all delimit at the whitespace when it comes to the gap on the answer

what?? I think you may be confused. getline() ignores spaces and stops reading then either '\n' is encountered or the input buffer fills up (assuming you are using character array).

is it possible to use a cast??

No.

maybe to take it in like a string but store in a char array cause the ifstream wont like me input like a string

This might be a lot easier if you would use std::string instead of character arrays

std::string answers;
std::string key;
std::string line;
ifstream in("datafile.txt");
getline(in, key);
while( getline(in, line) )
{
    answers = line.substr(0,15);
    name = line.substr(15);
}

what?? I think you may be confused. getline() ignores spaces and stops reading then either '\n' is encountered or the input buffer fills up (assuming you are using character array).


No.

This might be a lot easier if you would use std::string instead of character arrays

std::string answers;
std::string key;
std::string line;
ifstream in("datafile.txt");
getline(in, key);
while( getline(in, line) )
{
    answers = line.substr(0,15);
    name = line.substr(15);
}

THANKS!!!
I'm such an idiot as soon as I started thinking string and using the code u suggested i can read them in perfectly and use an iterator to cycle through the key and answers to generate my other data

thanks again and I'll post if I have any more troubles.

-James

Ok, so I will retire for the night it's late and I've got a small headache from burning through texts and webpages as well as write as much code as i could to understand what I have to do.

This is working code, I left the iterators in there just because this is all I know at the moment.

Thanks Ancient Dragon and Vernon for the help

here's the code.... and BTW I'm still very much interested in hearing other ideas of this code.

#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
using std::fixed;
using std::ios;
using std::left;
using std::right;

#include <iomanip>
using std::setprecision;
using std::setw;

#include <fstream>
using std::ifstream;
using std::ofstream;

#include <string>
using std::string;
using std::strcpy;

#include <cstdlib>
using std::exit;

int main()
{
	ifstream inQuiz("quiz.dat", ios::in);//create ifstream object
	ofstream outQuiz("Reports.txt", ios::out);//create ofstream object
	
	if(!inQuiz)//if quiz.dat file did not open correctly
	{
		cerr <<"File could not be opened." << endl;
		exit(1);
	}
	
	if(!outQuiz)//if Reports.txt did not open correctly
	{
		cerr <<"File could not be opened." << endl;
		exit(1);
	}
	//input headers to outQuiz object
	outQuiz << left << setw(16) << "Name" << setw(9) << "Correct"
		 << right << setw(9) << "Missing" << setw(10) << "Wrong"
		 << setw(10) << "Average" << endl << endl;
	
	string answers, key, line;
	getline(inQuiz, key);//gets answer key
	
	while(getline(inQuiz, line))//while inQuiz still has lines
	{
		//assign answer and name
		string answer = line.substr(0, 15);
		string name = line.substr(15);
		//creating char *, using c_str() function
		//for null terminated c-string
		char *keyIt = new char[key.length()+1];
		char *ansIt = new char[answer.length()+1];
		strcpy(keyIt, key.c_str());
		strcpy(ansIt, answer.c_str());
		
		double missing, correct, wrong, average;
		missing = correct = wrong = average = 0.0;
		//for loop calculating missing, correct and wrong
			for(int i = 0; i < 15; i++)
			{
				if(ansIt[i] == ' ')
					missing++;
				else if(answer[i] == keyIt[i])
					correct++;
				else
					wrong++;
			}
		
		average = (correct * 100) / 15;
		//output to outQuiz 
		outQuiz << setprecision(0) << left << setw(15) << name << right 
				<< setw(8) << fixed << correct<< setw(11) << missing 
				<< setw(10) << wrong << setw(10) << setprecision(1)
				<< average << endl;
			
		//release dynamic memory
		delete [] keyIt;
		delete [] ansIt;
	}
	//close file
	inQuiz.close();
	outQuiz.close();
	return 0;
}

-James

P.S. sorry for the massive includes my teacher wont let us use namespace std; more than likely till the new edition of the text we use comes out that will widely use namespace.

Edited 6 Years Ago by James.Butcher: n/a

lines 56-60 are unnecessary. Just use the std::string version. No need to convert to char*.

line 61: Why declare those as doubles? missing, correct, and wrong will never have decimals, so you might as well declare them as int (or long if you want). Typecast them to double when using them in division algorithms.

This question has already been answered. Start a new discussion instead.