I am learning from C++ Primer Plus book, and I've recently done this exercise from the book. I have a problem: when the user hits enter without any sign, then in the next entry to display any of these functions he has to hit enter again, because if not it'll still display "Wrong choice" and "Next Choice:" all the time. Can you tell me what's wrong with this code, and what should I add?
Thanks in advance.
/*When you join the Benevolent Order of Programmers, you can be known at BOP
meetings by your real name, your job title, or your secret BOP name.Write a program
that can list members by real name, by job title, by secret name, or by a member’s
preference. Base the program on the following structure:
// Benevolent Order of Programmers name structure
struct bop {
char fullname[strsize]; // real name
char title[strsize]; // job title
char bopname[strsize]; // secret BOP name
int preference; // 0 = fullname, 1 = title, 2 = bopname
};
In the program, create a small array of such structures and initialize it to suitable
values. Have the program run a loop that lets the user select from different alternatives:
a. display by name b. display by title
c. display by bopname d. display by preference
q. quit
302 Chapter 6 Branching Statements and Logical Operators
Note that “display by preference” does not mean display the preference member; it
means display the member corresponding to the preference number. For instance, if
preference is 1, choice d would display the programmer’s job title.A sample run
may look something like the following:
Benevolent Order of Programmers Report
a. display by name b. display by title
c. display by bopname d. display by preference
q. quit
Enter your choice: a
Wimp Macho
Raki Rhodes
Celia Laiter
Hoppy Hipman
Pat Hand
Next choice: d
Wimp Macho
Junior Programmer
MIPS
Analyst Trainee
LOOPY
Next choice: q
Bye!*/
Solution:
#include <iostream>
void text();
void name();
void title();
void secret();
void prefr();
const int strSize = 23;
const int People = 4;
char ch;
struct bop {
char fullname[strSize]; // real name
char title[strSize]; // job title
char bopname[strSize]; //secret BOP name
int preference; // 0 = fullname, 1 = title, 2 = bopname
};
bop people[People] //array of 4 structures
{
{"Tony Hawk", "Junior Programmer", "Novice",2}, //first member
{"Bill Gates", "Founder of Microsoft", "Billionaire",1}, //second member
{"Pop Leather", "Graphic Designer", "Fast and Furious",2}, //third member
{"Steve Jobs", "Apple Leader", "Undead Dragon",0} //fourth member
};
int main()
{
text(); //call a text function
std::cin.get(ch); //get a character
int i=0;
while(ch!='q')
{
switch(ch)
{
case 'a':
name();
break;
case 'b':
title();
break;
case 'c':
secret();
break;
case 'd':
prefr();
break;
default: std::cout << "Wrong choice\n";
}
std::cout << "Next choice: \n";
std::cin.get();
std::cin.get(ch);
}
std::cout<<"Bye!";
return 0;
}
void text()
{
std::cout<<"Benevolent Order of Programmers Report\n"
"a. display by name b. display by title\n"
"c. display by bopname d. display by preference\n"
"q. quit\n"
"Enter your choice:";
}
void name()
{
for(int i=0;i<People;i++)
std::cout<<people[i].fullname<<std::endl;
}
void title()
{
for(int i=0;i<People;i++)
std::cout<<people[i].title<<std::endl;
}
void secret()
{
for(int i=0;i<People;i++)
std::cout<<people[i].bopname<<std::endl;
}
void prefr()
{
for(int i=0;i<People;i++)
{
if(people[i].preference==0)
std::cout<<people[i].fullname<<std::endl;
else if(people[i].preference==1)
std::cout<<people[i].title<<std::endl;
else if(people[i].preference==2)
std::cout<<people[i].bopname<<std::endl;
}
}
I think the problem lies here:
std::cin.get();
std::cin.get(ch);
If there was indeed a character, the first get will clean the newline, and the second will perform another read.
If there was no character to begin with, the first get will consume the actual input, and ch end up as a newline.
A solution to that is: don't treat input as valid if you're not sure it's valid. In particular, you're expecting two characters of input: any character except newline followed by newline.
There are two rather simple ways to solve your problem:
Don't use characters: simply work on std::string and treat empty string as invalid.
Check if the first character was a newline and don't skip an additional character then.
A more advanced solution would be to experiment more with functions. Could you wrap the input to return optional<char>? Or even better, optional<Choice>, where Choice is an enum class?
Or perhaps you could create a function that loops automatically, prompting for a proper input every time, and separate it from the main program logic?
Related
class password
{
private
string password;
public:
void input();
}
void password::input()
{
cout<<" Enter your string :";
getline(cin,password);
}
void main()
{
getline(cin,password);// taking inout first time
cin >> ch;// switch case input
switch (ch)
{
case 1:
p.input(); // taking inout second time
break;
case 2:
// everything works fine in case 2
default:cout << "\n ... ";
}
}
As you can see above the code where I made a class called password with a member function input which when called takes input from user(string inputs).
Now there is no problem in compilation of the code, the problem is when I take input from the user . For first time it takes input properly but as soon as user inputs 1 for switch case as i have commented up there line of control comes in switch and if user tries to enter a string it takes one single word instead of whole string saying progran finished
P.s I have not written the whole program right here
Also, getline function works properly when i take first input but something gets messy inside the switch case for (ch=1)
Please provide me with some solution for this which would solve this problem.
I believe the issue is on your code with switch-case statement where you are using 1, for the compiler it stands for true(which is always true because the compiler only knows 0 and 1, 0 is false and 1 is true) flag. Try with "ONE" and "TWO" in your switch statement. I hope it will work.
I'm working on a project that basically writes into a file the contents of some objects's fields data I've created(one of them being pers1, of the class PERSON);
I've inserted data into the field members of the object pers1, and I opened a file, trying to write the content of those field members(string name, surname and unsigned int age) into the file, using file.write function. It wrote parts of the contents, inbetween alot of garbage. Please help me write the proper code, so that I can write each person details into the file in a consecutive way. Thank you
#include<iostream>
#include<string>
#include<fstream>
#include <sstream>
#include <iomanip>
#include <list>
class PERSON
{
string name;
string surname;
unsigned int age;
public:
void inputinfo()
{
cin>>name;
cin>>surname;
cin>>age;
}
outputinfo()
{
cout<<name;
cout<<surname;
cout<<age;
}
};
class STUDENT: public PERSON
{
int ID;
float marks_sum;
string belonging_class;
public:
inputinfo()
{
cin>>name;
cin>>surname;
cin>>age;
cin>>ID;
cin>>marks_sum;
cin>>belonging_class;
}
};
void writeinfile()
{
PERSON pers1;
ofstream file1
file1.open("Students.txt", std::ofstream::out | std::ofstream::app);
pers1.inputinfo();
file1.write(pers1.c_str(),pers1.length()); // this is the second aproach I've found on internet, but it gives errors;
file1.write((char*)&pers1, sizeof(pers1)); // this one is puting alot of garbage into the file, alongside fields data.
fisier.close();
}
int main
{
int opt1, opt2;
char option;
switch(opt1)
{
case 1:
do
{
cout<<endl;
<<"Choose one of variants"<<"1.Students"<<"2.Teachers"<<"3.Get back to main menu"<<endl;
cin>>opt2;
switch(opt2)
{
case 1:
do
{
cout<<"Do you wish to introduce a new student(Y/N)?";
cin>>option;
if(option!='N')
writeinfile()
} while(option!='N');
break;
default:
cout<<"Incorect!"<<endl;
}
while(opt2!=3);
break;
case 2: "...."
;
break
case 3: "...."
;
break;
}
}
}
I expect clean write of field data into the file, everytime I call the aforementioned function.
For example for 1st iteration, when I enter data into the object's field: name : John, surname: Doe, age: 45, I espect to see this data into a single line in the file(and no garbage inbetween).
#include <iostream>
#include <fstream>
std::ostream& operator<< (std::ostream& os, const PERSON& value )
{
// here, you decide how the output format should look like. See below.
// ...
return os;
}
std::istream& operator>> (std::istream& is, PERSON& value )
{
// here, you do the reverse of what you chose in operator<<(). See below.
// ...
return is;
}
While you will be able to quickly hack an implementation to those 2 functions, it is worth while thinking of the requirements of what you want to accomplish:
Maintenance? What happens to your files in the future when you change your PERSON (e.g. extra fields)? Do you still want to be able to use those old files?
Robustness. Will you have to pay attention to localization? What will happen if your first Chinese Student arrives with a Kanji name? Do you need utf8 encoding or alike? Will you encounter "missing values"?
Scalability? Will you end up writing your own little data base with your own SQL to later on query for subsets of persons? Will you still be able to read the whole file once it has grown beyond expectations? Will new files with other data arrive and later there will be the need to associate them? (OUTER JOIN, INNER JOIN,...)
As you can see from this short and certainly incomplete list, you are at a cross road here: Use database instead? Use a standard serialization format like XML or JSON or protocol buffers or FastNProto or whatever is fashionable today? Or just go ahead and do your own thing as it is all a quick and dirty thing anyway?
Now, to the practical things:
Inside your operator<<, you can "dump" your elements like this (sticking to what you wrote in the question):
os << "name: " << value.name.c_str()
<< ", surname: " << value.surname.c_str()
<< ", age: " << value.age
<< std::endl;
And to read it back in, you implement the operator>> accordingly.
std::string tag;
is >> tag; // optionally make sure it is "name:"
is >> value.name;
// ... (The commas might need some fiddling... well you wanted help not a full solution...)
Then, all you need to do is test it, e.g. using a string stream, serialize a person, read it into another instance and compare. And you should be done ;)
I am trying to control the usage of a small console application, and don't want the user to type anything more than one character. What I have so far
int main()
{
char choice;
string strChoice;
/*Set the title bar title to Binary Calculator*/
system("title Binary Calculator 2014");
do{
Menu();
getline(cin,strChoice);
choice = toupper(strChoice[0]); //convert value to uppercase for conformity
DetermineChoice(choice);
} while (mistakes < 3);
}
But when I type
bbbbbbbb
The screen goes buggy as all hell ( I believe it's caused from the do while loop ) so I need to just flush all characters besides the first one. Also when I select B the first time the program runs, then I go back and try to select B again it says I don't have anything but a carriage return in the input buffer.
Above is my int main. I'll show you the determine choice function and the error handling function.
void DetermineChoice(char value)
{
/*
Purpose: Determine what character was passed to value
Pre: a hopefully valid character
Post: Will go through cases below and then pass to another function
*/
string binary;
int decimal;
switch (value)
{
case 'B':
case 'C':
ConversionOperation(value);
case 'P':
cout << "Process File" << endl;
break;
case '+':
case '-':
case '/':
case '*':
case '%': ArithmeticActions(value);
case 'Q':
PrintSummary();
break;
default:
HandleChoiceError(value);
break;
}
}
Choice Error:
void HandleChoiceError(char value)
{
/*
Purpose: Handles the errorenous character passed
Pre: an invalid character
Post: Will output the error, pausing the screen then reprint the menu
*/
system("cls");
mistakes++;
cout << setw(40) << "The option you selected (" << value << ") is not a valid choice." << endl;
cout << setw(25) << "You have made " << mistakes << " mistake" << (mistakes > 1 ? "s" : "") <<" becareful only three allowed!" << endl;
cout << setw(60) << "Please press enter and try again" << endl;
if (mistakes < 3)
{
system("pause");
}
}
Some things that need to be aware of::
I can only use system (so please don't tell me it's bad or resource hog!)
I can't use fflush or any flushing besides cin.clear()
I can't use any other libraries besides iostream , iomanip , fstream , string , and ctype.h
Thanks everyone I now have the program working correctly.
int main()
{
char choice;
string strChoice;
/*Set the title bar title to Binary Calculator*/
system("title Binary Calculator 2014");
do{
Menu();
if ( getline(cin, strChoice) )
{
choice = toupper(strChoice[0]);
DetermineChoice(choice);
cin.ignore(10000, '\n');
}
else
{
cout << "Something went wrong with the input, please restart the program and try again." << endl;
break;
}
} while (mistakes < 3);
return 0;
}
There is no such thing as "flushing" an std::istream. Flushing is an output concept. Since there are multiple buffers for input from the console (the input buffer inside the std::istream's std::streambuf and an operating system consolde buffer) there is no reliable way to actually get rid of all input characters. You can get rid of all characters by disabling the concole's buffer (on UNIXes you'd use tcsetattr() and tcgetattr() to clear the ICANON flag).
The approaches which should be good enough for you needs are to either ignore all charactesr on the current line or to remove all characters in the input buffers:
To remove all characters on the currently you'd use std::istream::ignore() with the maximum number of characters to be ignored and the character where to stop, i.e., '\n' for the newline. To match as many characters as needed, you'd pass the magic value std::numeric_limits<std::streamsize>::max() (a reasonably large value, say 10000, would do the trick for practical needs, too).
You can find out a lower bound of characters immediately available using in.rdbuf()->in_avail(). This function determines how many characters can be read without the stream blocking. In practice, this is the number of characters in the std::istream's input buffer, i.e., something like in.ignore(in.rdbuf()->in_avail()) should remove all characters.
Pesronally, I would go with using in.ignore(count, '\n') with a suitable count (I'd obviously use std::numeric_limits<std::streamsize>::max() but it seems you can't use this function, probably because I'm currently helping with your homework assignment). Of course, std:getline() already reads the entire line anyway, i.e., there isn't really anything to be ignored.
Note that you should always verify whether the input operation was actuall successful:
if (std::getline(std::cin, line)) {
// process successful line
}
else {
// deal with the input having failed
}
Note, that input of a line is successful even when the line is empty! Before accessing the first character you should verify that it is present, e.g., using !line.empty().
BTW, as you have mentioned clear(): that actually doesn't ignore any characters. Instead, it clears the stream error flags, i.e., what is being tested to verify if input is successful. While being on notes: the argument to any of the <cctype> functions has to be a non-negative int in the value range of unsigned char plus EOF. Since char tends to be signed, passing an arbitrary char, e.g., strChoice[0] can result in undefined beheavior (e.g. when passing an ISO-Latin-1 or UTF-8 representation of my second name). The normal fix is to use
std::toupper(static_cast<unsigned char>(strChoice[0]))
Easiest to patch in: Replace
cin.ignore(1);
with
cin.ignore(numeric_limits<streamsize>::max(), '\n');
and
#include <limits>
at the top of the file. What this means is: "ignore all characters in the stream up to the next newline character (\n)". This is because
cin.ignore(n, c);
means "ignore n characters from cin, but stop after you found c," and numeric_limits<streamsize>::max() is the largest value in the streamsize type and a special case for istream::ignore where it means infinity.
This will work nicely for your use case because user input is generally line-based. It is better than discarding the input buffer because what's in the input buffer is not always predictable (someone could pipe a file to your program, the terminal could buffer strangely, the user could be a very fast typist, among other things). Discarding the input buffer wholesale will sometimes yield strange results, but nobody will be terribly surprised if you discard the rest of the line after a command.
So I'm working on a homework assignment for my CS162 class which requires me to make a program that allows the user to input their class plan for college. The user inputs classes they have taken, are currently taken, and/or plan on taking, with the categories of: department/class number, class name, term/year, whether or not the class is required for their major, and any additional comments. Then, the program is supposed to store this invermation with external data files so that the classes are stored and won't be lost. The program should be able to store up to 60 classes in memory.
I know how to create arrays of strucs and I know the basics behind external files, but I guess I'm having trouble combining the two (I'm a newbie here, so sorry if this is really basic!)
Here's what I have so far:
struct college_class
{
char dept_classnumber;
char class_name;
char term_year;
char is_required;
char comments;
char grade;
}
college_class[60]
int main()
{
int n;
char again;
for(n=0;n<60;n++)
{
do
{
cout<<"Enter department and class number (e.g. CS162): ";
getline (cin,college_class[n].dept_classnumber);
cout<<"Enter class name (e.g. Intro to Computer Science): ";
getline (cin,college_class[n].class_name);
cout<<"Enter the term and year the class was/will be taken: ";
getline (cin, college_class[n],term_year;
cout<<"Enter whether or not this class is required for your major: ";
getline (cin,college_class[n],is_required);
cout<<"Enter any additional comments here: ";
getline (cin, college_class[n],comments);
cout<<"Would you like to enter another class?(y/n)";
cin>>again;
}
while(again == 'y' || again == 'Y' && i<60)
}
Is this the right direction in terms of getting the user input? My other question is, how do you incorporate the external file into this so that everything the user inputs is stored into the file? Sorry if this is a little vague, and I'm obviously not looking for my homework to be done for me - I'm just looking for a little direction to get started here.
I know that writing on a text file looks like this, for example:
ofstream my file ("example");
if(myfile.is_open()))
{
myfile <<"blah blah blah. \n";
myfile.close();
}
...I'm just not sure how to make this work for arrays of structs.
There are multiple things wrong with you code.
First of all, you have to create a variable for your college_class array.
Eg.:
college_class myCollegeClass[60]
and use that when asking input
getline (cin, myCollegeClass[n].term_year;)
you accidentally used commas on some lines there, watch out for that
Furthermore, a char can only hold one character, which won't be enough if you want to hold the full class name, use strings in your struct.
struct college_class
{
string class_name;
...
}
You used a nested loop there, which will repeat your questions 60 times, regardless if you said you didn't want to input anything else.
I'd suggest
int i=0;
char again = 'y';
while(again != 'n' && again != 'N' && i<60)
{
...
i++
}
As for the file, after you have your inputs, just loop though your myCollegeClass array and write the data to the file. Eg.:
myfile << myCollegeClass[i].class_name;
I am trying to make a program to print the names of an array, the array represents the first row of the chess board. My attempt is with the following program but the only thing that I can achieve is printing numbers :( .
#include <iostream>
using namespace std;
void main()
enum pions // giving the names in pions
{
Tower=1 ,
Horse ,
Officer ,
Princes ,
King };
int Chess [0][8]={Tower , Horse , officer , Princes , King , Officer , Horse , Tower };
// putting the names of each coordinate in the array (above ) .
cout << " in this place the pion that is placed is the " << chess [0][1] << endl;
I know that the program will print the number 2 , but how can I make it print the word "Horse" instead of 2 ???
The cout command is written by the creator of the program (me :P) how i can give the option to the user to choose the part of the array that is going to be printed ?
Thanks in advance for your time and effort, a big sorry in case I am not clear, its my first post :D .
Any recommendations are welcome.
You need to write a function that takes a parameter of type Chess and returns a string. Here's one that would work:
const char *chessPieceName(Chess piece)
{
switch(piece) {
case Tower:
return "Tower";
break;
case Horse:
return "Horse";
break;
// etc.
default:
return "Not a valid piece";
break;
}
}
You can call then call this function from main:
cout << " in this place the pion that is placed is the " << chessPieceName(chess[0][1]) << endl;
Having said that, you have numerous other issues in your code that should prevent it from compiling. I'm not going to go through them, since you seem to just be interested in the question you asked.
You could have a translator function that takes the int value and transforms it into a printable string value:
string translate(int piecenum){
string [5] ref = { "Tower", "Horse", "Officer", "Princes" "King"}
return ref[piecenum];
}
You can print them out using cout << translate(chess[0][1]) << endl
You cannot print out identifiers of the enum.
You need to create a different function which takes your enum as an input parameter and returns an std::string with the name you want. For example, if you pass it pions::Tower, it will return "Tower". If you pass it pions::Horse, it will return "Horse". Etc.
Hint: Use a switch statement in the body of that function, as godel9's answer illustrates.
Either use C# instead of C++ or define yourself an array of chess figure names. For example
enum pions // giving the names in pions
{
Tower = 0 , // <== I prefer to use 0 instead of 1
Horse ,
Officer ,
Princes ,
King };
const char *name = { "Tower", "Horse", "Officer", "Princes", "King" };
//...
for ( int i : chess[0] ) std::cout << name[i] << std::endl;