Reading from a File

Hello, I am attempting to create a smaller version of the Eliza program. I have a bit done, however I am confused at how to read from a file based off whether there is a space in front of the line or not and then given a response-the line with the space, to the question-the one without the space. The idea is to look through the file, and if the user inputs a question from the file, it will put out a random response affiliated with that question. Here is what I have so far.

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
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
using namespace std;

int main()
{
	srand(unsigned(time(NULL)));
	rand();
	string question[100];
	string answer[100];
	int count = 0;
	string filename("text.txt");
	//cout << "Please enter a filename: ";
	//cin >> filename;
	ifstream infile(filename);
	if (infile.fail())
	{
		cout << "Error: main(): Failed to open file: ";
		cout << filename << endl;
	}
	else
	{
		do
		{
			while (!infile.eof())
			{
				string phrase;
				getline(infile, phrase);
				if (!infile.eof())
				{
					if (phrase[0] == ' ')
					{
						answer[count++] = phrase;
					}
					else
					{
						question[count++] = phrase;
					}
				}
				string question;
				cout << "Please ask a question: ";
				cin >> question;
				while (question != phrase)
				{
					cout << "What is wrong with you?! Ask something relivant!: ";
					cin >> question;
				}
				if (question == phrase)
				{
					int pick = rand()/double(SHRT_MAX)*count;
					cout << answer[pick] << endl;
				}

			}
			infile.close();	
		} while (!infile.eof());
	}

	cout << endl;
	system("pause");
	return 0;
}


I am confused about recognizing the question and grabbing the response that is liked to the question. The file might look something like this:

How are you?
_I am fine.
_I am well.
_Not good.
What are you?
_A monster.
_A dog.
(_ indicates a space because it won't allow a space on here when submitting)
I don't want the whole thing solved, I am just trying to give enough info for the correct help. Thanks!
Read the file in as a binary. The streams won't skip spaces then if you give it the right functions/commands. You might have to fight with the end of line markers yourself though. if you use infile.open("filename.txt", std::ios::stuff) instead of letting the object creation do it for you; you have a lot more control over the situations with the file. Especially how the file is read in.

There are things I can add to the stream to make it not skip white space, which is the case you are fighting. Like infile >> noskipws; to help out.
Last edited on
You should replace !infile.eof() with infile anywhere it occurs in your code. Lines 27, 31, and 58.

You can completely get rid of the do-while loop.

While it's not bad to close the file explicitly, there's really no need since the destructor will do it for you.

You should use getline on line 48.

On to the problem:

Let me restate it so I (and you) can be sure I understand. You want your program to prompt a user for a question. You then want your program to present the user with an answer based on the answers listed in your file for that specific question. The organization of the file is such that lines that indicate questions do not begin with a space and those that should be answers do begin with a space. Your problem is that you are unsure how to associate the questions/answers after reading them from the file?

Where you are going wrong:

The questions and answers must be read in, in their entirety, before you start asking for user input.

There is, in your code, no association between the questions and answers array (ie. there's no way to tell which answers belong to which questions.)

I would begin by defining a data structure:

1
2
3
4
5
6
struct QnA
{
     string question ;
     string answers[5] ;
     unsigned n_answers ;
};


I use an array for answers, because I don't know if you're familiar with the std::vector type which would be a much better fit. Of course, you should modify the size of answers to something that is appropriate for your program.

Then you may use an array of QnA:

QnA QandA[100] ;

which you populate with the information from your file.

When that's done, you ask your user for a question. loop through QandA to find it and randomly choose one of the answers.
Last edited on
The concept is to have a file, like so:

1
2
3
4
5
6
7
8
9
Hello
 Hi.
 Hola.
How are you?
 Good.
 Bad.
Who are you?
 Bob. 
 George.


The program is to have the user put in a question, find the question in the file, and given a random answer BASED on the responses below THAT answer. Then it loops back and asks again as many times as the users wants, and they can end by hitting crtl^z. Here is what I have done/modified. So far I can get it to do the first question in the small file I have, and give the first response, not a random one of the 3 I have. The file I use is the one above.

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
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
using namespace std;

int main()
{
	srand(unsigned(time(NULL)));
	rand();
	string answer[100];
	int count = 0;
	string filename("text.txt");
	//cout << "Please enter a filename: ";
	//cin >> filename;
	ifstream infile(filename);
	if (infile.fail())
	{
		cout << "Error: main(): Failed to open file: ";
		cout << filename << endl;
	}
	else
	{
		while (!cin.eof())
		{
			while (!infile.eof()) 
			{
				string phrase;
				string question;
	
				cout << "Please ask a question: ";
				getline(cin, question);

				getline(infile, phrase);
				if (question != phrase) 
				{
					//cout << "Please enter a valid question!: ";
					//getline(cin, question);	
				}
				if (question == phrase)
				{
					getline(infile, phrase);
					if (phrase[0] == ' ')
					{
						answer[count++] = phrase;	
					}
					int pick = rand()/double(SHRT_MAX)*count;
					cout << answer[pick] << endl << endl;
				}
				infile.close();
			}
		}
		cin.eof();
	}




	cout << endl;
	system("pause");
	return 0;
}
Aha. I was misled by the presence of the questions array.

The approach I described earlier is less complex.

So, there is no point to having the answers array in the scope that it's in. It should be defined in the inner loop. I'm going to suggest adding a function to reduce the complexity of main. This function should take a pointer to (an array of) type std::string, an unsigned value specifying the size of the array, and a std::string representing the question to search for in the file. It should return the number of answers found in the file.

unsigned get_answers(std::string answer_list[], unsigned list_size, std::string question)

This function should open the file and search for the question. If the question is found, it should read in the answers following it and store them in answer_list. Then it should return the number of answers read into the answer list.

Your main would become:

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
int main()
{
	srand(unsigned(time(NULL)));
	rand();

	for ( ; ; )
	{
		string question ;
		cout << "Please ask a question: " ;

		getline(cin, question) ;

		if ( question.size() == 0 ) // user just entered '\n' ;
			break ;                 // use it as our cue to quit.

		string answers[100] ;
		unsigned n_answers = get_answers( answers, 100, question ) ;

		if ( 0 == n_answers )
		{
			cout << "Question not found.\n" ;
			continue ;
		}

		cout << answers[rand()%n_answers] << '\n' ;
	}
}


Now, in the function where you read from the file, do not check for eof. If you encounter eof you've already had an input operation fail and you didn't catch it. Instead, check the general state of the object (eg. if ( infile ).)

You can use infile.peek() to check if the next character in the file is a space without extracting it.


Last edited on
Topic archived. No new replies allowed.