I dont understand .txt file and what is wrong

I am trying to load an exam and have it read from a separate .txt file. I don't know how to make it work. I am using Cygwin and have code and have a .txt file. I made it in wordpad. It wont talk to the source code. Do I not use wordpad. I see it using excel a lot but I don't know how that works either. I want the code to load an exam and ask the user questions. The user can tell the code which question to ask then it will make the question based on the .txt file. Here is the code I am using.


here is the .txt file.

3
TF 5
There exist birds that cannot fly?
true
MC 10
Who was the President of the USA in 1991?
6
Richard Nixon
Gerald Ford
Jimmy Carter
Ronald Reagan
George Bush Sr.
Bill Clinton
E
TF 10
The city of Boston hosted the 2004 Summer Olympics?
false

I just have no idea where to go from here. I have tried changing the int main to work differently but when I am changing my code around I just cause more errors.

Thank you for any help


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

//question class
class Question
{
protected:
string question;
int value;
public:
Question(string theQuestion, int pointValue)
{
question = theQuestion;
value = pointValue;
}
string getQuestion(string qu)
{
question = qu;
return question;
cout << "\t" << question << endl;
}
int getValue(int val)
{
value = val;
return value;
cout << "Worth " << value << " points" << endl;
}
virtual ~Question(){}
virtual string printOptions()
{
return NULL;
}
virtual string getAnswer()
{
return NULL;
}
}; //end question class

//true false class
class QuestionTF : public Question
{
private:
string answer;
public:
QuestionTF(string theQuestion, int pointValue, string theAnswer) : Question(theQuestion, pointValue)
{
answer = theAnswer;
}
string printOptions() const
{
cout << "\t(true/false)" << endl;
return NULL;
}
string getAnswer(string ans)
{
answer = ans;
return answer;
cout << "\tAnswer - " << answer << endl;
}
}; //end true false class

//multiple choice class
class QuestionMC : public Question
{
private:
string answer, options[];
public:
QuestionMC(string theQuestion, int pointValue, string theAnswer) : Question(theQuestion, pointValue)
{
theAnswer = answer;
}
string addOption(string anOption)
{
for (int i = 0; i < 4; i++)
{
anOption = options[i];
}
return 0;
}
string printOptions()
{
return 0;
}
string getAnswer(string ans)
{
ans = answer;
return answer;
cout << "\tAnswer - " << answer << endl;
}
}; //end multiple choice class

//function to read the bank in
void testBank()
{
//declarations
int numQuestions, numOptions, points;
string questionType, questionText, answerText, mcOption;

//arrays
string questionBank[numQuestions] = {}, answerBank[numQuestions] = {}, mcOptions[numOptions] = {};

//class objects
Question test(questionText, points);
QuestionTF tf(questionText, points, answerText);
QuestionMC mc(questionText, points, answerText);

//read number of questions
fstream infile;
infile >> numQuestions;

//loop for test questions
for (int i = 1; i <= numQuestions; i++)
{
infile >> questionType >> points;
cout << "Question " << i << ":" << endl;
test.getValue(points);
test.getQuestion(questionType);

//true/false
if (questionType == "TF")
{
getline(infile, questionText);
getline(infile, answerText);
test.getQuestion(questionText);
tf.printOptions();
tf.getAnswer(answerText);
}//end true/false

//multiple choice
else if (questionType == "MC")
{
getline(infile, questionText);
infile >> numOptions;
for (int j = 1; j <= numOptions; j++)
{
getline(infile, mcOption);
mc.addOption(mcOption);
}
getline(infile, answerText);
mc.getAnswer(answerText);
}//end multiple choice
}//end loop
};//end test bank function

int main()
{
//open test bank file
ifstream infile;
infile.open ("testbank.txt");

//error if file can't open
if (!infile)
{
cout << "File could not be opened" << endl;
}
testBank();
infile.close();

return 0;
}//end main 
string answer, options[];
If you use C-style arrays, you need to inform the compiler how large they are as soon as you declare them. As far as I can see, it’s the same error which repeats throughout the code. Maybe there are others, but you can solve this one by turning your C-style arrays into std::vectors.

May I ask you if you wrote that code or you copied it from somewhere? Because it looks too an obvious issue for such an evolved code with inheritance.
Hello Tappy,


3                                   // <--- Number of questions in the file.

TF 5                                // <--- Type and point value.
There exist birds that cannot fly?  // <--- Question.
true                                // <--- Answer.

MC 10
Who was the President of the USA in 1991?
6
Richard Nixon
Gerald Ford
Jimmy Carter
Ronald Reagan
George Bush Sr.
Bill Clinton
E

TF 10
The city of Boston hosted the 2004 Summer Olympics?
false


As you can see the input file has a pattern I would work on reading the file and figuring out how you want to store the information you read in.

I agree with Enoizat the using a vector would be easier because you do not have to worry about the size when you define the variable. With a C-style array you will need to either use a constant value like "5" or whatever size is needed or define something like constexpr MAXSIZE{ 5 }; and define the variable like std::string choices[MAXSIZE]{ "" }; unless this is a private variable of a class then you would leave off "{ "" }"

Break the program into smaller pieces and work on one piece at a time and you will find it easier to figure out.

Hope that helps,

Andy

P.S. This is not the first program like this to be dealt with here. The trick is finding the right key words to search on.
Hello Tappy,

To help get you started:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
	std::ifstream infile;
	infile.open("testbank.txt");

	//error if file can't open
	if (!infile)
	{
		std::cout << "File could not be opened" << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(3));  // Requires header files "chrono" and "thread"
		exit(1);
	}

	testBank(infile);  // <--- Send "infile". Pass by reference.
	
	infile.close();

        return 0;
}


The change I made to the if statement will allow the program to pause so you can see the message. The "exit(1);" will end the program early because there is no reason to go any farther.

As I started to work with the "testBank" function I found that the classes are not quite right and the "testBank" function needs some work.

Hope that helps,

Andy
I did not write it I was using it as an example. My main question is how do I get the code to read a .txt file? Where do I place it and would code like this actually read it? I am using wordpad and trying to get the code to just read the .txt file.

Thank you for your responses.
Hello Tappy,

I do not mean to put you off, but I will have to get back to you after dinner.

The code you have used is a good starting point, but needs major work to make it work. I will explain shortly.

Andy
Where do I place it and would code like this actually read it? I am using wordpad and trying to get the code to just read the .txt file.

In order to give you some good advice, I think we need to clarify what you're trying to do.
May I ask you what are your skills in programming, more or less?
Do you know any programming language?
Do you know C++ in particular? At what level?
I mean, it sounds a bit odd to start from a code with classes and inheritance and later need to discuss about how to read from a file; most programmers, when they met classes, have already dealt with input and output from files.

For example, can you guess more or less what the following code does?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <fstream>
#include <iostream>
#include <string>

int main()
{
    std::ifstream in("testbank.txt");
    std::ofstream out("wordlist.txt");
    std::string word;
    while(in >> word) {
        out << word << '\n';
    }
    in.close();
    out.close();

    return 0;
}


This is not a test :-) I just think, if the above code isn’t clear, there’s no point in talking about classes.
Hello Tappy,

Earlier I showed you what main should look like. That has not changed.

I will start with the header file that I called "Classes.hpp". I like to put things like classes and prototypes in in a header file. For the most part the classes are OK, but needed some improvement. I will also mention that I put some of the function definitions in s separate .cpp file.

This will give you an idea of what I changed notice the comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef _CLASSS_ 
#define _CLASSS_

//question class
class Question
{
protected:
	std::string m_question;
	int m_points;
public:
	Question(std::string theQuestion, int pointValue);
	//std::string getQuestion(std::string qu);  // <--- Bad format and bad function. Not used.
	//int getValue(int val);  // Did not use this one either.
	virtual ~Question();
	void SetQuestion(std::string question);  // <--- Added.
	void SetPoints(int points);  // <--- Added.

//    These functions are not needed in this class. They work better in the other two classes.
//	virtual std::string printOptions()
//	{
//		return NULL;
//	}
//	virtual std::string getAnswer()
//	{
//		return NULL;
//	}
}; //end question class

#endif // end !_CLASSS_ 


The declaration of the dtor is fine his way when it is a base class inherited by another class.

The two set functions should be set up this way and should oly do one thing set the private member variable of the class.

The "Get..." function is the opposite and its only function is to return a private member variable value.

The other two classes are similar except that they only need one "Set..." function and I did not use any "Get..." functions.

In all the classes I changed the private variables to start with "m_" to make it easier to know it is part of a class. I do not know how strict this is but it seems to be a common convention for naming class variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void testBank(std::ifstream& infile)  // <--- Passed by reference.
{
	//declarations
	//constexpr int numQuestions{ 3 };  // <--- in the end not needed.
	//constexpr int numOptions{ 6 };  // <--- in the end not needed.
	// Always initialize your variables when you define the.
	int	points{ 0 }, numberOfQuestions{ 0 }, numberOfOptions{ 0 };
	std::string questionType{ "" }, questionText{ "" }, answerText{ "" }, mcOption{ "" };

	//arrays improper definition and not used.
	//std::string questionBank[numQuestions] = {}, answerBank[numQuestions] = {}, mcOptions[numOptions] = {};

	//class objects
	//Question test(questionText, points);  // <--- Does not need to be defined here.
	QuestionTF tf(questionText, points, answerText);  // <--- This defines both QuestionsTF and Questions.
	QuestionMC mc(questionText, points, answerText);  // <--- This defines both QuestionsMC and Questions.
	// <--- Both objects of Question are different.

	//read number of questions
	//std::ifstream infile;  // <--- Duplicate definition of infile. Not needed here.
	infile >> numberOfQuestions;


Referring to the above code:

Lines 4 and 5 I added to work with line 11 until I realized that none of those variables on line 11 were never used. More importantly you are tying to create an array with a variable that has no value yet and defining an array this way is not allowed. Lines 4 and 5 fixed that problem. If you look at the code on line 11 you are trying to use a variable, e.g., "numQuestions" that does not get a value until line 21.

Line 14 is not needed because "QuestionsTF" and "QuestionsMC" both create an object of "Questions"

line 21 will read from the file the first line which in this case is 3 into "numberOfQuestions" or you could easily use the original "numQuestions".

After this I kept the for loop and most of the original code, but did make chnges. Some are commented out as you can see here others I added.

1
2
3
4
5
6
7
8
9
10
11
12
	for (int i = 1; i <= numberOfQuestions; i++)
	{
		infile >> questionType >> points;
		std::cout << "Question " << i << ":" << std::endl;
		tf.SetPoints(points);
		//test.getValue(points);
		//test.getQuestion(questionType);
		//std::cout << std::endl;  // <--- Used in testing as  break point.
		//true/false
.
.
.


I added line 5 because that is the way it should be done.

Lines 6 and 7 are not used because these functions are wrong and there is no reason to "Get" anything here.

In the first if statement:

1
2
3
4
5
6
7
8
9
		if (questionType == "TF")
		{
			infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
			std::getline(infile, questionText);
			std::getline(infile, answerText);
.
.
.


I added line 3 because of line 3 in the previous bit of code. After reading the point value of the question the "\n" is left in the input buffer and needs to be cleared before a "getline()" otherwise the "getline()" will only read the "\n" from the buffer and not the file.

I will give you a chance to figure out the rest of the if statement.

The If statement for "MC" will start the same way reading the question then it changes to deal the possible answers ending with reading the answer. In between you will have to "Set"the member variables of the classes. I used a for loop to deal with the possible answers. Keep in mind that the array to hold these answers starts at index zero not one. And in your original the condition of the for loop says "<=" this will crash your program when it runs because the last number for a subscript will be 6, as I setup the array to be a size of 6, a 6 as the subscript will put this element outside the upper bound of the array thus crashing the program. The for condition only needs to be "<" to work properly.

This may not be exactly what you want, but when you get this working it would not be that hard to change it around.

I noticed you are using WordPad. Do not use this as it is not a pure ASCII text editor. Notepad would be better, but still not the best. Installing either Visual Studio, I would suggest the 2017 version, or the latest version of Code::Blocks would be the best. If all you want is a simple text editor do a search for "gedit"

There is something to keep you busy for awhile,

Hope that helps,

Andy
Topic archived. No new replies allowed.