Transform this function to read the entire file - c++

I made a quiz program in c++. It's working but with separate blocks of instructions for every question in file. I need to transform the block of instructions(after for) to work for all questions.
The file look like this
1.WHEN COMPUTER WAS FIRST INVENTIONED?
a.1822
b.1823
c.1834
d.1922
2.WHO kILLED PRESEDENT BENOGIR VUTTO?
a.nawaz shrif
b.pervase
c.non of them
d.political leder
This is only a function in my program.
void Question::quiz(int &Total)
{
string line[200];
string answer;
string easy[15]={"a","c","a","a","b","c","d","c","a","b","b","c","c","c","a"};
ifstream fin("questions.txt");
if(!fin)
{
cout<<"Cannot open file\n";
exit(1);
}
cout<<"The first question is\n";
for(int contor=0;contor<5;contor++)
{
getline(fin,line[contor]);
cout<<line[contor]<<'\n';
}
cout<<"Select your answer: ";
cin >> answer;
if(answer==easy[0])
{
Total+=1;
}
cin.get();
}

You can use a while loop for reading the file upto end of line. As every block contains exactly five lines, you can take input for each line until you get the line size greater than 0. As blank line will also be in the input and you need to ignore them.
void Question::quiz(int &Total)
{
string line[200];
string answer;
string easy[15]= {"a","c","a","a","b","c","d","c","a","b","b","c","c","c","a"};
ifstream fin("questions.txt");
if(!fin)
{
cout<<"Cannot open file\n";
exit(1);
}
int cnt=0;
while(getline(fin,line[0]))
{
cout<<line[0]<<endl;
while(line[0].size()==0)
{
getline(fin,line[0]);
cout<<line[0]<<endl;
}
for(int contor=1; contor<5; contor++)
{
do
{
getline(fin,line[contor]);
}
while(line[contor].size()==0);
cout<<line[contor]<<'\n';
}
cout<<"Select your answer: ";
cin >> answer;
if(answer==easy[cnt++])total++;
line[0]="";
}
cout<<total<<endl;
}

Here is one was of doing it, making use of objects and vectors:
struct quiz_question
{
auto print_question() -> void
{
char prefix[] = { 'a', 'b', 'c', 'd' };
std::cout << "Question " << question_number << ": " << question << '\n';
for (auto x = 0; x < possible_answers.size(); x++)
{
std::cout << prefix[x] << ". " << possible_answers[x] << '\n';
}
}
auto check_answer(char user_answer) -> bool { return user_answer == answer; }
std::size_t question_number;
std::string question;
std::array< std::string, 4 > possible_answers;
char answer;
};
int main()
{
std::vector< quiz_question > questions;
std::string number_of_questions_str;
std::size_t number_of_questions;
std::ifstream ifs("questions.txt");
if (!ifs)
{
std::cout << "Cannot open questions.txt!\n";
exit(1);
}
// Start loading in questions.
// Read first line, describes how many questions there are.
std::getline(ifs, number_of_questions_str);
ifs.ignore(10000, '\n'); // Ignore a line
try
{
number_of_questions = std::stoul(number_of_questions_str);
}
catch (std::invalid_argument &ec)
{
std::cout << "Unable to parse questions.txt!\n";
exit(1);
}
// Load each question in.
for (auto x = 0; x < number_of_questions; x++)
{
quiz_question current_question;
current_question.question_number = x + 1;
// Read the question line
std::getline(ifs, current_question.question);
// Read the possible answers
for (auto &possible_answer : current_question.possible_answers)
{
std::getline(ifs, possible_answer);
}
// Read the actual answer
current_question.answer = ifs.get();
ifs.ignore(10000, '\n'); // Ignore the rest of that line
questions.push_back(std::move(current_question));
ifs.ignore(10000, '\n'); // Ignore a line
}
// Now all the questions have been loaded. Lets start the quiz!
char answer;
std::size_t score { 0 };
std::cout << "Starting the quiz!\n";
for (auto &question : questions)
{
question.print_question(); // Print question and possible answers
std::cin >> answer;
if (question.check_answer(answer))
{
std::cout << "Correct!\n\n";
score++;
}
else
{
std::cout << "Incorrect!\n\n";
}
std::cin.clear(); // Clear flags
std::cin.ignore(10000, '\n'); // Skip to next line
}
}
I've had to change your questions.txt format a little also, its described below:
questions.txt
2
WHEN COMPUTER WAS FIRST INVENTIONED?
1822
1823
1834
1922
a
WHO KILLED PRESEDENT BENOGIR VUTTO?
nawaz shrif
pervase
non of them
political leder
c
The first line is your total number of questions.
Blank line.
Question line
Answer A
Answer B
Answer C
Answer D
Correct answer
Blank line
Repeat numbers 3-9
Hope this helps

Related

File reading and Manipulation

I have a text file:
1
2
3
stop
4
The code has to add each number to the previous number to get a new value and it needs to stop when it reads the "stop" in the file.
For example output would be:
1
3
5
Reading has stopped
How can I break the code for my output to be like this?
The "reading has stopped", only has to appear when there is a 'stop' in the file. otherwise the output should just be numbers.
You can read each piece of the file into a string and end if the input is "stop". If the input isn't "stop" you can convert it to an int using std::stoi
#include <string>
#include <iostream>
#include <fstream>
int main() {
std::string numberString;
std::ifstream file{ "filename.txt" };
int previousNumber = 0;
while (file >> numberString)
{
if(numberString == "stop")
{
break;
}
try {
int number = std::stoi(numberString);
std::cout << (number + previousNumber) << " ";
previousNumber = number;
} catch(...) {
std::cout << "invalid number" << std::endl;
}
}
file.close();
std::cout << "Reading has stopped" << std::endl;
}
If your text file has only one string "stop", then there's a very easy solution: you just keep reading integers until the reading fails
int main() {
ifstream ifs("test.txt");
int first = 0;
int second;
while (ifs >> second) {
cout << first + second << ' ';
first = second;
}
cout << "Reading has stopped" << endl;
return 0;
}
The problem with this solution is that if you have other strings in the text file and you want to handle them in a different way, this solution will fail.
Hope it helps.

How to read a file line by line and separate the lines components?

I am new to C++ (I usually use Java) and am trying to make a k-ary heap. I want to insert values from a file into the heap; however, I am at a loss with the code for the things I want to do.
I wanted to use .nextLine and .hasNextLine like I would in Java with a scanner, but I am not sure those are applicable to C++. Also, in the file the items are listed as such: "IN 890", "IN 9228", "EX", "IN 847", etc. The "IN" portion tells me to insert and the "EX" portion is for my extract_min. I don't know how to separate the string and integer in C++ so I can insert just the number though.
int main(){
BinaryMinHeap h;
string str ("IN");
string str ("EX");
int sum = 0;
int x;
ifstream inFile;
inFile.open("test.txt");
if (!inFile) {
cout << "Unable to open file";
exit(1); // terminate with error
}
while (inFile >> x) {
sum = sum + x;
if(str.find(nextLin) == true //if "IN" is in line)
{
h.insertKey(nextLin); //insert the number
}
else //if "EX" is in line perform extract min
}
inFile.close();
cout << "Sum = " << sum << endl;
}
The result should just add the number into the heap or extract the min.
Look at the various std::istream implementations - std::ifstream, std::istringstream, etc. You can call std::getline() in a loop to read a std::ifstream line by line, using std::istringstream to parse each line. For example:
int main() {
BinaryMinHeap h;
string line, item;
int x sum = 0;
ifstream inFile;
inFile.open("test.txt");
if (!inFile) {
cout << "Unable to open file";
return 1; // terminate with error
}
while (getline(inFile, line)) {
istringstream iss(line);
iss >> item;
if (item == "IN") {
iss >> x;
sum += x;
h.insertKey(x);
}
else if (item == "EX") {
// perform extract min
}
}
inFile.close();
cout << "Sum = " << sum << endl;
return 0;
}

C++: Hangman game

I'm in the process of writing the program and stumbled upon a few issues.
The code reads a text file, which contains a list of 20 string words. The function playHangman() is supposed to read the file and randomly picks one word, which is displayed as asterisks in the console. The code works fine with local words when the function is called. For example Playhangman("stackOverflow"), will show the exact number of characters and will loop through them until the word is guessed right. If you take a look at the code, I'm calling the random word into the function. That word is stored as array. I know that that's not the proper way to do randomize, but for now, even if it picks the same word over and over, that's Ok, I just need to make sure it actually reads the array. The other thing is, when all characters are revealed, all the words on that text file are displayed, looks like I'm calling the entire content of the array instead of just that random word that's supposed to be generated.
Any help would be appreciated, thank you!
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string word[20];
string randomWord = word[rand() % 20];
int playHangman(string randomWord) {
int misses = 0;
int revealedletters = 0;
string display = randomWord;
ifstream textFile;
// Open file
textFile.open("hangman.txt");
// Check if file exists
if (!textFile) {
cerr << "Unable to open text file\n";
exit(1); // Call system to stop
}
else {
cout << "File opened successfully. Program will continue...\n\n";
// Loop through the content of the file
while (textFile >> randomWord) {
for (int i = 0; i < display.length(); i++)
display[i] = '*';
while(revealedletters < randomWord.length()) {
cout << "Misses: " << misses << endl;
cout << "Enter a letter in word ";
cout << display << " : ";
char response;
cin >> response;
bool goodGuess = false;
bool duplicate = false;
for (int i = 0; i < randomWord.length(); i++)
if (response == randomWord[i]) {
if (display[i] == randomWord[i]) {
cout << response << " is already in the word." << endl;
duplicate = true;
break;
}
else {
display[i] = randomWord[i];
revealedletters++;
goodGuess = true;
}
}
if (duplicate)
continue;
if (!goodGuess) {
misses++;
cout << response << " is not in word\n";
}
}
cout << "You guessed right! The word is " << randomWord << ".\n";
}
return misses;
}
}
// TODO: Do you want to guess another word, Y/N?
int main () {
playHangman(randomWord);
// TODO: number of misses to guess the word.\n";
}
In your declaration and initialisation of your global variables:
string word[20];
string randomWord = word[rand() % 20];
word is an array of 20 empty strings and therefore randomWord will also always be empty.
In your playHangman function you have:
while (textFile >> randomWord) {
for (int i = 0; i < display.length(); i++)
display[i] = '*';
while(revealedletters < randomWord.length()) {
......
This reads a single word from the file into randomWord, plays the game with that word then loops round to read the next word and plays again with that word. As revealedletters doesn't get reset the game will finish immediately if the first word is longer than the next one.
I think the code you actually want looks something like this (remove your global variables too):
std::string word;
std::vector<std::string> words;
while (textFile >> word) {
words.push_back(word);
}
randomWord = words[rand() % words.size()];
std::string display = std::string(randomWord.size(), '*');
while(revealedletters < randomWord.length()) {
......
If you really must use arrays:
const size_t maxWords = 20;
std::string words[maxWords];
size_t wordCount = 0;
while (textFile >> word && wordCount < maxWords) {
words[wordCount++] = word;
}
randomWord = words[rand() % wordCount];
std::string display = std::string(randomWord.size(), '*');
while(revealedletters < randomWord.length()) {
......

Get only integers as input, code not working as expected

I am trying to allow only integrers to be inputted
Should Reject:
5h
3.4
3.gh
3.0
htr
Should Accept:
-5
0
78
Current Code
int getIntInput() {
int userInput;
while (true) {
std::cout << "> ";
std::cin >> userInput;
std::cout << std::flush;
if (std::cin.fail()) {
std::string cinBuffer;
std::cin.clear();
std::getline(std::cin, cinBuffer);
continue;
}
break;
}
return userInput;
}
Updated Code
Issues:
Accepts all rejections excluding "htr" (No numerals)
int getIntInput() {
std::string rawInput;
int parsedinput;
while (true) {
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
try {
parsedinput = std::stoi(rawInput);
} catch (std::invalid_argument & e) {
continue;
} catch (std::out_of_range & e) {
continue;
}
break;
}
return parsedinput;
}
Finished Code
Accepts only integers with an optional parameter which will allow
negative numbers to be accepted or rejected.
int getIntInput(bool allowNegatives = true) {
bool validIntInput;
std::string rawInput;
int parsedinput;
while (true) {
validIntInput = true;
// Grabs the entire input line
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
for (int i = 0; i < rawInput.length(); i++) {
// Checks to see if all digits are a number as well as to see if the number is a negative number
if (!isdigit(rawInput[i]) && !(allowNegatives && i == 0 && rawInput[i] == '-')) {
validIntInput = false;
break;
}
}
if (!validIntInput) {
continue;
} else {
try {
// Try parse the string to an int
parsedinput = std::stoi(rawInput);
// Catch all possible exceptions, another input will be required
} catch (...) {
continue;
}
// If the code reaches here then the string has been parsed to an int
break;
}
}
return parsedinput;}
The way cin works when having to read an int, is that it starts parsing the integer and stops when it finds a non-int character, storing the parsed int in the variable. That's why on float numbers it stops on the dot and in input '5g' it will stop on g.
What you can do instead if you only want integer input, is to read the whole line and then check if every character in your string is numeric, with the following piece of code:
bool onlyNums = true;
for (int i=0;i<rawInput.size();i++) {
if (!isdigit(rawInput[i]))
onlyNums = false;
}
if (!onlyNums)
continue;
(You have to include ctype.h library for the above code)
If you don't mind the overhead, I would grab the input from cin with cin.getline() and save it into a string. Then loop through the string and call isdigit on each char. You can discard the chars that aren't digits by useing the str.erase function.
You will need to #include cctype for isdigit().
Note: this will have at least O(N) runtime based on the length of your string.
template<class T>
T findInStreamLineWith(std::istream &input) {
std::string line;
while (std::getline(input, line)) {
std::istringstream stream(line);
T x;
if (stream >> x >> std::ws && stream.eof()) {
return x;
}
// reenter value
// cout << "Enter value again: ";
}
throw std::invalid_argument("can't find value in stream");
}
…
auto x = findInStreamLineWith<int>(std::cin);

Why is a space in the string making my code loop infinitely? [duplicate]

This question already has answers here:
Infinite loop with cin when typing string while a number is expected
(4 answers)
Closed 6 years ago.
I have the following code which simply takes a string and find each character's index in the alphabet.
void encrypt()
{
string alpha = "abcdefghijklmnopqrstuvwxyz";
string word;
vector<char> temp;
char a, b;
cout << "Enter string to encrypt: \n";
cin >> word;
for (int i=0; i<word.length(); i++)
{
bool t = false;
a = word[i];
for (int j=0; j<alpha.length(); j++)
{
b = alpha[j];
if (a == b)
{
cout << a << "'s index = " << j+1 << endl;
t = true;
}
}
if (t == false)
{
cout << "space here\n";
}
}
}
when i input a word/string with no space the code works fine but when i input a string with a space the program goes into an infinite loop.
edit main() added due to request:
main()
{
int a;
bool b = false;
while (b == false)
{
cout << "1. Encrypt a string\n";
cout << "2. Decrypt a string\n";
cout << "3. Exit\n";
cout << endl;
cin >> a;
cout << endl;
if (a == 1)
{
encrypt();
}
else if (a == 2)
{
decrypt();
}
else if (a == 3)
{
b = true;
}
}
return 0;
}
cin >> word;
will read only the first word and leave the second word in the input stream. After that, the call
cin >> a;
will result in an error unless the second word starts with a number. Once the program enters a state of error, nothing is read and the program stays in a loop.
To diagnose problems like these, always check the state of the stream after a read operation.
if ( cin >> word )
{
// Use word
}
else
{
// Deal with error.
}
if ( cin >> a )
{
// Use a
}
else
{
// Deal with error.
}
To address your real problem, don't use operator>> to read space separated string. Use getline (and use a variable name different from word).
std::string str;
if ( getline(std::cin, str) )
{
// Use str
}
else
{
// Deal with error.
}
However, in order to use getline successfully, you have to make sure that after a is read, you ignore the rest of the line. Otherwise, the rest of the line will be read by getline.
if ( cin >> a )
{
// Ignore rest of the line
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Use a
}
else
{
// Deal with error.
}
Replace cin >> word; with getline(cin, word);. It will accept a line as input. Which will resolves your input containing spaces.
As far as infinite loop concern, clear the error bits on the stream cin.clear();
You can check whether cin is accepting the space separated string completely, by doing a cout instantly after the cin. If cin is not accepting the space separated string, then try using getline
Issue resolved:
Use the following:
cout << "Enter string to encrypt: ";
scanf(" %[^\n]s",word);
for (int i=0; word[i]!='\0'; i++)
{
use
include <cstdio>
Hope this solves the problem!! I will get back to you with the solution using string..