Reading strings and integers into arrays from .txt file - c++

I am a novice programmer and I'm having trouble with one of my exercises. I need to create a program that reads the student information (SSN, First & last name, test score) from a .txt file called "students.txt", and then uses four global parallel arrays to store the student information. I'm required to use an int array to store SSN, a string array to store first names, a string array to store last names, and a double array to store scores. The operations include listing the students' info like this exactly how it appears:
SSN Last-Name First-Name Score
628130189 James, Paul 92.0
237698211 Cook , Daniel 86.0
201895367 Garza, Melessa 78.0
491066285 Barbara, Jessica 62.0
168606868 Bruce, Elizabeth 90.0
378205732 Lee, Sarah 91.5
118453900 Brian, David 87.0
583192186 Garza, Cody 92.0
226665118 Lewis, Gage 78.0
175382843 Collins, James 69.5
816231095 White, Ann 88.5
376651608 Jackson, Mark 72.0
508234567 Freeman, Mark 86.0
763211099 William, Jack 52.0
286204723 Rodriguez, John 69.5
But of course, the .txt file is just SSN First-name last-name and score unaligned and separated by comas. Then I have to display the student with the highest score, and then the lowest, and the average of all scores using "void". I just dont know how to get it to read the file in the first place and make the data so that it's organized into columns.
Also...I have to report an error message and exit the program immediately if the file cannot be found? Otherwise, the program should display a main menu as follows to allow the user to complete the listed
operations.
Here's what I have so far:
#include <iostream>
#include<iomanip>
#include<fstream>
using namespace std;
void mainmenu();
/*
void sort_name();();
void sort_ssn();
void sort_score ();
*/
void average();
void lowest_score();
void highest_score();
void open_file();
const int totNum = 15; //my global variables
int ssn[totNum];
string fname [totNum];
string lname [totNum];
double score [totNum];
int main ()
{
char choice;
ifstream fin;
fin.open("students.txt")
for (1=0; i <= totNum; i++)//this is where I'm stuck
{
?????
}
do
{ mainmenu(); //calling main menu funtion
cin >> choice;
cin.ignore (10, '\n'); //spacing
switch(choice) // switch statment for multiple cases for flexibility
{
case 'l':
case 'L':
case '1': open_file(); break;
/*
case 'h':
case 'H':
case '2': highest_score(); break;
case 'o':
case 'O':
case '3': lowest_score(); break;
case 'a':
case 'A':
case '4': average(); break;
case 's':
case 'S':
case '5': sort_ssn(); break; //operation 5-7 is a bonus work
case 'n':
case 'N':
case '6': sort_name(); break;
case 'c':
case 'C':
case '7': sort_score();break;
*/
case 'e':
case 'E':
case '0': break;
default: cout << "Wrong choice!" << endl; break;
}
cout << endl;
} while(choice != '0'); // && choice !='E' && choice!='e')
}
void mainmenu() //output main menu
{
cout << "Main Menu (Assignment 8)" << endl;
cout << "1. List students' infromation (L)" << endl;
/*
cout << "2. Find the highest score (H)" << endl;
cout << "3. Find the lowest score (O)" << endl;
cout << "4. Calculate the average score (A)" << endl;
cout << "5. Sort students by SSN (S)" << endl;
cout << "6. Dort students by name (N)" << endl;
cout << "7. Sort students by score (C)" << endl;
*/
cout << "0. Exit" << endl;
cout << "Please select an option: ";
}
it doesn't make any sense now but that's why I'm desperate for help...

Related

C++ Newbie: Why don't these nested loops work for this program?

I'm in the middle of taking an online C++ course, and I've been having issues with this homework problem. I tried reaching out to my professor twice, but he hasn't responded. I've sought out many solutions, but since I'm new in the course, many of the solutions involve using techniques I haven't learned yet (like character arrays.) I can get the conversion program to work, but I want the program to allow to process as many user inputs as the user wants.
When I run the program, the program accepts my first input that is 'y' or 'Y' to run the program. It then will ask for a string to convert to the telephone number. This works. However, I need the program to ask the user if they want to run the program again to convert another string to a telephone number or to terminate the program.
I put in another cin at the end of the first while loop to prompt for another input, but it gets skipped over everytime and keeps doing the while loop.
Question: Why is the last prompt to repeat the program get skipped every time I've run it? What am I missing?
Here's the problem and what I've done so far:
Problem:
To make telephone numbers easier to remember, some companies use
letters to show their telephone number. For example, using letters,
the telephone number 438-5626 can be shown as GET LOAN.
In some cases, to make a telephone number meaningful, companies might
use more than seven letters. For example, 225-5466 can be displayed as
CALL HOME, which uses eight letters. Instructions
Write a program that prompts the user to enter a telephone number
expressed in letters and outputs the corresponding telephone number in
digits.
If the user enters more than seven letters, then process only the
first seven letters.
Also output the - (hyphen) after the third digit.
Allow the user to use both uppercase and lowercase letters as well as
spaces between words.
Moreover, your program should process as many telephone numbers as the
user wants.
My code so far:
#include <iostream>
using namespace std;
int main()
{
char letter, runLetter;
int counter = 0;
cout << "Enter Y/y to convert a telephone number from letters to digits"
<< endl;
cout << "Enter any other key to terminate the program." << endl;
cin >> runLetter;
while (runLetter == 'y' || runLetter == 'Y')
{
cout << "Enter in a telephone number as letters: " << endl;
while (cin.get(letter) && counter < 7 )
{
if (letter != ' ' && letter >= 'A' && letter <= 'z')
{
counter++;
if (letter > 'Z')
{
letter = (int)letter-32;
}
if (counter == 4)
cout << "-";
switch (letter)
{
case 'A':
case 'B':
case 'C':
{
cout << "2";
break;
}
case 'D':
case 'E':
case 'F':
{
cout << "3";
break;
}
case 'G':
case 'H':
case 'I':
{
cout << "4";
break;
}
case 'J':
case 'K':
case 'L':
{
cout << "5";
break;
}
case 'M':
case 'N':
case 'O':
{
cout << "6";
break;
}
case 'P':
case 'Q':
case 'R':
case 'S':
{
cout << "7";
break;
}
case 'T':
case 'U':
case 'V':
{
cout << "8";
break;
}
case 'W':
case 'X':
case 'Y':
case 'Z':
{
cout << "9";
break;
}
default:
break;
}
}
}
cout << endl;
cout << "To process another telephone number, enter Y/y" << endl;
cout << "Enter any other key to terminate the program." << endl;
cin >> runLetter;
}
cout << "Goodbye. " << endl;
return 0;
}
Thanks in advance for any help. I know this might be an easy solution, but I've been tinkering with this program for a couple of days now.
Tried moving the last user prompt in and out of each if/else structure and different while loops. Not sure what I can do to make the program take a new input after the first iteration.
A very good hint to your problem is the comment from #AlanBirtles. Also I know you are a beginner and you may not know about std::string but you should use it because you are learning C++ not C. in a nutshell, it is a C++ way of dealing with char arrays, also better than just that.
Here is your code with minimum changes to do what you are looking for. The main changes is the use of std::string, the use of std::getline and the definition of the counter inside the while loop.
#include <iostream>
#include <string>
using namespace std;
int main()
{
char letter;
std::string runLetter;
std::string number;
cout << "Enter Y/y to convert a telephone number from letters to digits"
<< endl;
cout << "Enter any other key to terminate the program." << endl;
std::getline( std::cin, runLetter);
while (runLetter == "y" || runLetter == "Y")
{
int counter = 0;
cout << "Enter in a telephone number as letters: " << endl;
std::getline(std::cin, number);
for (int i = 0; i < number.size(); i++)
{
letter = number[i];
if (counter < 7)
if (letter != ' ' && letter >= 'A' && letter <= 'z')
{
counter++;
if (letter > 'Z')
{
letter = (int)letter - 32;
}
if (counter == 4)
cout << "-";
switch (letter)
{
case 'A':
case 'B':
case 'C':
{
cout << "2";
break;
}
case 'D':
case 'E':
case 'F':
{
cout << "3";
break;
}
case 'G':
case 'H':
case 'I':
{
cout << "4";
break;
}
case 'J':
case 'K':
case 'L':
{
cout << "5";
break;
}
case 'M':
case 'N':
case 'O':
{
cout << "6";
break;
}
case 'P':
case 'Q':
case 'R':
case 'S':
{
cout << "7";
break;
}
case 'T':
case 'U':
case 'V':
{
cout << "8";
break;
}
case 'W':
case 'X':
case 'Y':
case 'Z':
{
cout << "9";
break;
}
default:
break;
}
}
}
cout << endl;
cout << "To process another telephone number, enter Y/y" << endl;
cout << "Enter any other key to terminate the program." << endl;
std::getline(std::cin, runLetter);
}
cout << "Goodbye. " << endl;
return 0;
}
I found the following errors in your code:
You do not reset the variable counter to 0 in the second loop iteration, so that counter has the value 7 in the entire second loop iteration, which causes the inner while loop not to be entered. This bug should be clearly visible when running your program line by line in a debugger while monitoring the values of all variables.
You always read exactly 7 characters from the user per loop iteration, which is wrong. You should always read exactly one line per loop iteration. In other words, you should read until you find the newline character. You can ignore every character after the 7th letter, but you must still consume them from the input stream, otherwise they will be read in the next loop iteration(s), which you do not want. However, this does not mean that you can simply ignore everything after the 7th character, because the number of characters is not necessarily identical to the number of letters. For example, if a 7 character string has one space character, then it only has 6 letters. As stated in the task description, the user should be allowed to enter spaces in the string.

Is there a way to input different value for the same data when the same function is called multiple times?

I'm creating a student data management program in C++ and the function to insert examination marks is buggy.
The code given below is enough to recreate the buggy part of the program.
I have tried to increase the size of sub[] to 16
I have tried to insert data one after the other instead of a loop
None of the above seem to solve the problem
Menu function:
char ch;
main_menu:
clrscr();
cout << "Press the key for your choice:\n";
cout << "D -> Edit details\n";
cout << "R -> Get result\n";
cout << "I -> Insert marks\n";
cout << "E -> Exit Program";
choice:
ch = getch();
switch(ch)
{
case 'd':
//edit_nam(); Ignore this one
goto main_menu;
break;
case 'i':
ins_mar();
goto main_menu;
break;
case 'r':
//get_res(); This one is not related to the problem
goto main_menu;
break;
case 'e':
break;
default:
goto choice;
}
Insert marks function:
for(int i = 0; i < 6; i++)
{
clrscr();
cout << "Enter details of subject:" << i + 1;
cout << "\nSubject name:";
cout << "\nMarks:";
gotoxy(14, 1);
cin.getline(student.marks[i].sub, 8);
gotoxy(7,2);
cin >> student.marks[i].mark;
(i != 5) ? cout << "\nPress any key to continue..." : cout << "\nPress any key to return to menu...";
getch();
}
Student structure:
struct stu
{
char name[20];
int ID;
int cls;
mar marks[6];
};
Marks structure:
struct mar
{
char sub[8];
float mark;
}
If the code was working fine, then it would ask the user to enter the marks for all six subjects, every time the function is called in one run.
However, It is not so. In the first time of function call, everything happens in the correct manner, but it does not ask for subject name after first subject in any of the other runs.

Reading digits and converting to words

I'm new to c++ and I have to write a program that takes a user 4-digit number and convert it to words i.e. 7238 would be wrote as seven two three eight. Yet it writes every number as unknown. Any advice for a noob would be greatly appreciated.
#include iostream
using namespace std;
int main() {
char number;
cout << "Please enter a 4 digit number: ";
cin >> number;
switch(number){
case 1 :
cout<< "one";
break;
case 2 :
cout<< "two";
break;
case 3 :
cout<< "three";
break;
case 4 :
cout<< "four";
break;
case 5 :
cout<< "five";
break;
case 6 :
cout<< "six";
break;
case 7 :
cout<< "seven";
break;
case 8 :
cout<< "eight";
break;
case 9 :
cout<< "nine";
break;
case 0 :
cout<< "zero";
break;
default :
cout << "UNKNOWN.";
}
}
Sounds like homework but here are some tips. Change your number variable to type of int You can break the number out into individual variables with division and modulus. I would stuff those into an integer array.
int array[4];
arr[0] = (number / 1000) % 10; // Thousands
....... // You do the hundreds and tens
arr[3] = (number % 10); // Ones
Then use a loop around your switch statement where your counter is less than 4 (the length of the array). Make sure to increase your counter at the end of each loop. Oh, and it's #include <iostream>.
With to_string and range based for:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int number;
cout << "Enter the number: ";
cin >> number;
string strnum = to_string(number);
for (auto c : strnum)
{
switch (c)
{
case '0': cout << "zero "; break;
case '1': cout << "one "; break;
case '2': cout << "two "; break;
case '3': cout << "three "; break;
case '4': cout << "four "; break;
case '5': cout << "five "; break;
case '6': cout << "six "; break;
case '7': cout << "seven "; break;
case '8': cout << "eight "; break;
case '9': cout << "nine "; break;
default: cout << "non-digit"; break;
}
}
return 0;
}
You need to put ascii values in your case statements. Currently you are comparing the ascii values for digits with numbers 0 - 9.
Values can be found here : http://www.asciitable.com/
Your variable is of type char. A char stores a character, usually ASCII encoded. If the user inputs a '1', for example, that would usually translate to an integer value of 49, not 1. Either read into an int or change your case labels to use character literals:
case '1':
cout << "one";
break;
You could then use a loop to read multiple digits.

Loop in a switch case statement

I need help with validating a switch case statement i need it to check what the user has entered and if it does not match reject it and tell them to do it again. the one i have at the moment partially works but will reject the first number then break when trying to enter another number. Help If you need to see the whole program just ask.
#include "stdafx.h"
#include "cstdlib"
#include "iostream"
#include "windows.h"
#include "cmath"
using namespace std;
int main(int argc, char* argv[])
{
float ALT0();
float ALT5000();
float ALT10000();
float ALT15000();
float ALT20000();
float ALT25000();
float ALT30000();
float ALT35000();
float ALT40000();
void an_answer(float a);
char Restart;
char op;
float answer;
do
{
cout << "\n\t\t\tOperational Flight Plan\n" << endl;
cout << "For the saftey of everyone on board, there will be 100 kg added to the overall\namount to give the aircraft more fuel incase of a head on wind or delays at the landing airport.\n" << endl;
cout << "Select Altitude the aircraft will fly at: " << endl;
cout << "0 for 0ft\n1 for 5000ft\n2 for 10000ft\n3 for 15000ft\n4 for 20000ft\n5 for 25000ft\n6 for 30000ft\n7 for 35000ft\n8 for 40000ft" << endl;
cin >> op;
switch (op)
{
case'0':
answer=ALT0();
break;
case '1':
answer=ALT5000();
break;
case '2':
answer=ALT10000();
break;
case '3':
answer=ALT15000();
break;
case '4':
answer=ALT20000();
break;
case '5':
answer=ALT25000();
break;
case '6':
answer=ALT30000();
break;
case '7':
answer=ALT35000();
break;
case '8':
answer=ALT40000();
break;
default:
cout << "You must enter a number from 0-8" << endl;
cin >> op;
break;
}
an_answer(answer);
cout << "Do you want to do another calculation? Press Y for yes and anything else to quit.";
cin >> Restart;
} while (Restart=='y' || Restart=='Y');
//system("PAUSE");
//return EXIT_SUCCESS;
}
I'm betting you're hitting enter after entering the number. Your first cin >> op reads the number, but your second one reads the enter key. If you want to read in an entire line, use a function that reads in an entire line.
Alternately, move the second cin >> op up to before the switch statement. This will break if someone enters more than one character before hitting enter but will work otherwise.

Is there any way to make a C++ Switch Statement loop back to the first case?

OK, here is a simple code example:
char answer;
cin >> answer;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
break;
default :
cout << "\nThat was an invalid response.";
}
Obviously I could pull my hair out with a while(answer != 'Y' || answer !=...) But is there a more elegant way of simply returning to the first case after executing the default case? So if a user enters the wrong letter, I simply ask them the question again until they type an acceptable response?
No this isn't homework or anything. I'm working through Dawson's C++ Game Programming book, and I wanted to jazz up the program example a little by allowing the user to keep or trade an item. I got all that working beautifully, but if a wrong response is entered it just shows the contents of the inventory and exits. I wanted to get that right. Force the user to enter a correct response, then show the updated inventory afterwards.
Appreciate the help!
UPDATE!
You have all given me so many different approaches to this - I really appreciate it! I admit I probably did not design this switch statement correctly and I apologize for the contradiction. I will try each of your suggestions and post back here, choosing one as answer. Thank you!
OK, I have just gone through all of your answers, trying most of them with my code. I have chosen the simplest, most elegant solution as the answer to my question. But you all have helped me to see different ways of looking at this, and I understand so much more about switch statements now. Using it in fact in place of a while loop in a tutorial I am following right now at YouTube by user What's A Creel?
I really appreciate all your help! I feel that I have really accomplished a lot in my programming practice today. You guys (and gals) are all awesome!
UPDATED AND COMPLETE CODE:
#include <iostream>
#include <string>
using namespace std;
// This program displays a hero's inventory
int main()
{
const int MAX_ITEMS = 4;
string inventory[MAX_ITEMS];
int numItems = 0;
inventory[numItems++] = "Rusty Battle Axe";
inventory[numItems++] = "Leather Armor";
inventory[numItems++] = "Wooden Shield";
cout << "Inventory:\n";
for(int i = 0; i < numItems; ++i)
{
cout << inventory[i] << endl;
}
cout << "\nYou open a chest and find a Lesser Healing Potion.";
inventory[numItems++] = "Lesser Healing Potion";
cout << "\nInventory\n";
for(int i = 0; i < numItems; ++i)
{
cout << inventory[i] << endl;
}
cout << "\nYou also find a Short Sword.";
if(numItems < MAX_ITEMS)
{
inventory[numItems++] = "Short Sword";
}
else
{
cout << "\nYou have too many items and can't carry another.";
cout << "\nWould you like to trade the " << inventory[0] << " for the Short Sword? ";
}
while (true)
{
char answer;
cin >> answer;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest." << endl;
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest." << endl;
break;
default:
cout << "\nThat was an invalid response!";
cout << "\nWould you like to trade the " << inventory[0] << " for the Short Sword? ";
continue;
}
break;
}
cout << "\nInventory:\n";
for(int i = 0; i < numItems; ++i)
{
cout << inventory[i] << endl;
}
return 0;
}
You can use a one-shot loop that breaks at the end and use continue to jump back to the top:
while(true)
{
switch(...) {
//...
default:
continue;
}
break;
};
Perhaps a nicer way is to define a set of valid letters, especially if you'll do this kind of thing everywhere in your code:
char GetChoice( const string & prompt, const string & valid_choices )
{
while( cin.good() )
{
cout << prompt << " " << flush;
char c;
if( !cin.get(c) ) break;
size_t pos = valid_choices.find(toupper(c));
if( pos != string::npos ) return valid_choices[pos];
}
return 0; // Error condition.
}
And use like this:
switch( GetChoice("Do you want cake?", "YN") )
{
case 'Y':
cout << "Cake for you.\n";
break;
case 'N':
cout << "No cake for you.\n";
break;
case 0:
exit(1); // Error occurred
}
bool valid;
do
{
char answer;
cin >> answer;
switch (answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
valid = true;
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
valid = true;
break;
default :
cout << "\nThat was an invalid response.";
valid = false;
break;
}
}
while (!valid);
Well, add a loop and it will "loop back" wherever you want.
Note that the entire body of switch is just one long statement with labels in it. It works as any other statement, once you entered it through one of the labels. Just like an ordinary C++ statement will not "loop back" for you by itself unless you make it a cycle or use goto, neither will the body of switch "loop back" for you by itself.
So, if you want to transfer control back - use the appropriate language construct. You can inject goto right into the body of that statement and it will work as usual.
switch(answer)
{
case 'y':
case 'Y':
FIRST_OPTION:
...
break;
default :
...;
goto FIRST_OPTION; // Jump to first option
}
You might also want to take a look at Duff's device for a more intricate example of control transfer inside switch statement.
However, your question seems to contradict itself. You state that you want to ask the user for input again, if the answer was invalid. But the user input is requested and accepted outside of switch. Why do you say then that you want to return to the first option of switch???
Use a goto statement in the default section to go back to the input part
Here is one approach:
bool done = false;
while (!done) {
char answer;
cin >> answer;
done = true;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
break;
default :
cout << "\nThat was an invalid response.";
done = false;
}
}
Use a while or do while loop.
Eg:
char answer;
bool loopback = true;
do
{
cin >> answer;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
loopback = false;
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
loopback = false;
break;
default :
cout << "\nThat was an invalid response.";
loopback = true;
}
}
while (loopback);
You can use label and goto statement. Label the statement where you are asking the user to input and add a goto statement in default case.
Ex::
AskQuestion:
cout << "Press 'Y' for Short Sword Or 'N' for Rusty Battle Axe" << endl;
char answer;
cin >> answer;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
break;
default :
cout << "\nThat was an invalid response.";
goto AskQuestion ;
}
Alternative way is to use do-while loop with condition while(answer != 'Y' || answer !=...) as you have already commented in the question. Ex::
do{
cout << "Press 'Y' for Short Sword Or 'N' for Rusty Battle Axe" << endl;
char answer;
cin >> answer;
switch(answer)
{
case 'y':
case 'Y':
inventory[0] = "Short Sword";
cout << "\nYou place the Rusty Battle Axe in the chest.";
break;
case 'n':
case 'N':
inventory[0] = "Rusty Battle Axe";
cout << "\nYou leave the Short Sword in the chest.";
break;
default :
cout << "\nThat was an invalid response.";
}
}while( answer != 'Y' || answer != 'y' || answer != 'N' || answer != 'n' ) ;