c++ Microsoft visual studio on a windows.
im very new to coding. currently going through Programming -- Principles and Practice Using C++ by Stroupstrup and I came across a difficulty. I am to create a "score chart" with vector name and vector score from the user input. I used for-loop to get the input. now I am to modify the program so that with 2nd input from the user I can search the list and "cout<<" the score for a person. the problem is the the program completely ignores the 2nd "cin>>" command.
I search online and could not find a reasonable answer to this problem. Is there any special interaction between a for-loop input being terminated and another input (not looped)
syntax:
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> name;
vector<int> score;
string temp2;
int i;
for (string temp; cin >> temp >> i;) //input terminated with "Ctrl+Z"
name.push_back(temp), score.push_back(i);
for (int i = 0; i < name.size(); ++i) {
for (int j = i + 1; j < name.size(); ++j) {
if (name[i] == name[j]) {
name[j] = "error";
score[j] = 0;
}
}
}
for (int i = 0; i < name.size(); ++i) {
cout << name[i] << "------" << score[i] << "\n";
}
cout << "name"; //this line shows in the console
cin >> temp2; //but I cannot prompt the user to input again?
return 0;
}
CTRL-Z is interpreted as "End-Of-File", such that any subsequent access to this stream will not read in items any more. The only secure way is to change program logic such that the list of names is terminated by, let's say "END", and not a CTRL-Z. Then you can continue in a save manner.
Often input from a terminal is read in line by line and parsed afterwards. This makes error handling easier. See the following code following such an approach:
#include <sstream>
int main() {
string line;
map<string,int> scoreboard;
cout << "enter name score (type END to finish):" << endl;
while (std::getline(cin, line) && line != "END") {
stringstream ss(line);
string name;
int score;
if (ss >> name >> score) {
scoreboard[name] = score;
} else {
cout << "invalid input. Type END to finish" << endl;
}
}
cout << "enter name:" << endl;
string name;
if (cin >> name) {
auto item = scoreboard.find(name);
if (item != scoreboard.end()){
cout << "score of " << name << ":" << item->second << endl;
}
else {
cout << "no entry for " << name << "." << endl;
}
}
}
Related
I have some code that takes a list of names + double values from a .txt file and displays these in the command prompt. For this an array of structs is dynamically allocated. The code should know the size of the array based on the first value in the .txt file, which is then followed by the names and associated values. It should then display the list in two parts with names that have an associated double value higher than or equal to 10.000 listed first. If none of the values qualifies, it displays 'None' in the first half.
The program executes, but the debugger gives an exception and the output is not as expected.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:\n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}
The donorlist.txt file looks like this:
4
Bob
400
Alice
11000
But the output looks like this:
Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0
The exception that the debugger gives me is:
Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.
Now I assume something is going wrong with reading from the dynamically allocated memory. Maybe something is causing me to read from memory beyond the allocated array? I'm having trouble finding exactly where the mistake is being made.
Your problems begin with the wrong amount written in your data file.
Fix it with:
2
Bob
400
Alice
11000
They then continue with the fact that you inccorectly read the file.
Remember: Mixing operator>> and getline() is not as simple as it seems.
You see, operator>> IGNORES newline and space characters until it finds any other character.
It then reads the upcoming characters until it encounters the next newline or space character, BUT DOES NOT DISCARD IT.
Here is where the problem with getline comes in. getline reads EVERYTHING until it encounters newline or a specified delim character.
Meaning, that if your operator>> stops after encountering newline, getline will read NOTHING since it immediately encounters newline.
To fix this, you need to dispose of the newline character.
You can do this by first checking if the next character in the stream is indeed newline and then using istream::ignore() on it;
int next_char = stream.peek();
if(next_char == '\n'){
stream.ignore();
}
A working example of your code would be:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves '\n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case '\n': inFile.ignore();
break;
case EOF: cout << "Donor amount too big!\n";
exit(EXIT_FAILURE);
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
if(!grandpatrons_exist){
cout << "None\n";
}
cout << "Here's the list of Patrons:\n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
delete[] donors;
return 0;
}
Now, an even better solution would be to use vectors instead of raw pointers and implement operator>> and operator<< which would greatly simplify
the reading and printing of the objects.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case EOF: return stream;
case '\n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << '\n';
}
}
}
else{
cout << "None\n";
}
cout << "\nHere's the list of Patrons:\n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << '\n';
}
}
return 0;
}
Some other great improvements would be:
Use partition to seperate great patrons from normal ones.
Use stream iterators to read the objects into the vector.
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), '\n');
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(first_grand_patron == begin(donors)){
cout << "None!\n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << '\n';
}
cout << "\nHere's the list of Patrons:\n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << '\n';
}
return 0;
}
Now some general tips:
Struct/Class names should start with a capital letter.
Stop Using std::endl.
No need to cin.clear(). Cin is only used once and never again.
Use member-initializer lists.
Optionally use ++i instead of i++ in for loops to get used to the correct way of incrementing a variable unless needed otherwise.
bool grandpatrons is too much of an abstract name for a flag.
donors is a subjectively better name than short for donor list.
The program works all the way up until it checks for the name the user enters. When you enter the name you wish to search for in the array of structures that have been imported from a file full of customer info) it comes back segmentation fault core dumped. This puzzles me.
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
struct AccountsDataBase{
char name[50];
string email;
long int phone;
string address;
};
#define MAX 80
AccountsDataBase * account = new AccountsDataBase[MAX];
void readIn(ifstream& file){
int i=0;
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
}
void getAccount(){
char userPick[50];
char streamName[50];
cout << " What account will we be using? " << endl;
cin.getline(streamName, 50);
for(int i=0; strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
}
}
}
int main(){
ifstream file;
file.open("2.dat"); //opens data account records text
readIn(file);
getAccount();
delete account;
return 0;
}
Your loop keeps reading everything into the initial element of the array:
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
because the value of i is never incremented. You can convert this to a for loop, like this:
for (count = 0 ; count < MAX && !file.eof() ; count++) {
file >> account[count].name >> account[count].email >> account[count].phone >> account[count].address;
}
Note that I changed i to count:
AccountsDataBase * account = new AccountsDataBase[MAX];
int count = 0;
This will help you solve another problem - determining when the array ends in the getAccount function. Currently, you assume that the record is always there, so the outer loop keeps going on. Now that you have count, you could change the loop like this:
for(int i=0; i < count && strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
break;
}
}
if (i == count) {
cout << "Not found." << endl;
}
How can I read input line(type string) with whitespace? I tried getline but it goes into infinite loop. Following is my code.
#include <iostream>
#include <cstring>
#define MAX 50 //size of array
//Used G++ 4.6.3 compiler
using namespace std;
int main() {
struct Manager {
string name;
int age;
int working_years;
string phone;
int salary;
}info[MAX];
char inp; //To choose options
int array_pos = 0; //Current position in array of Manager structure
string search_name; //Manager name you want to search
cout << "Press 'i' to insert manager information or 's' to search for manager information by name or 'a' to abort: ";
cin >> inp;
while(inp != 'a') {
int search_num = 0; //array position at which search result is found
int found = 0;
if (inp == 'i' || inp == 's') {
if (inp == 'i') {
int k = array_pos;
cout << "Enter the information of the manager no "<<k+1<<" is : ";
cout << "Enter the Name : ";
//infinte loop occurs
getline(info[array_pos].name, '\n');
//cin >> info[array_pos].name;
cout<<"Enter manager age : ";
cin >> info[array_pos].age;
cout << "Enter manage working years : ";
cin >> info[array_pos].working_years;
cout << "Enter manager phone no. : ";
cin >> info[array_pos].phone;
cout << "Enter manager salary : ";
cin >> info[array_pos].salary;
array_pos++;
}
if (inp == 's') {
cout << "Enter the manager name you want to search : ";
cin >> search_name;
for(int i = 0; i < array_pos; i++) {
//using str1.compare(str2) to compare manager name
if(info[i].name.compare(search_name) == 0) { //manager name found in array of structure
found = 1;
search_num = i;
cout << "Name : " << info[search_num].name << "\n";
cout << "Age: " << info[search_num].age << "\n";
cout << "Working Years: " << info[search_num].working_years << "\n";
cout << "Phone No. : " << info[search_num].phone << "\n";
cout << "Salary : " << info[search_num].salary << "\n";
} //end of if loop for comparing string
} //end of for loop for searching
if(found == 0)
cout << "No Manager by this name exist in record" << "\n";
} //end of if loop
} //end of if loop for searching or insertion
if(inp == 'a')
break;
cout << "Press 'i' to insert manager information or 's' to search for manager information by name or 'a' to abort: ";
cin >> inp;
} //end of while loop
return 0;
}
How can I read input line(type string) with whitespace?
std::string line;
if (std::getline(std::cin, line)) {
...
}
Note that apart from checking the return value of std:getline call, you should also avoid mixing >> operator with std::getline calls. Once you decide reading the file line by line, it seems to be cleaner and more reasonable to just make one huge loop and do the additional parsing while using string stream object, e.g.:
std::string line;
while (std::getline(std::cin, line)) {
if (line.empty()) continue;
std::istringstream is(line);
if (is >> ...) {
...
}
...
}
Simplest way to read string with spaces without bothering about std namespace is as follows
#include <iostream>
#include <string>
using namespace std;
int main(){
string str;
getline(cin,str);
cout<<str;
return 0;
}
Solution #1:
char c;
cin >> noskipws; // Stops all further whitespace skipping
while (cin >> c) { // Reads whitespace chars now.
count++;
}
Solution #2:
char c;
while (cin.get(c)) { // Always reads whitespace chars.
count++;
}
To practice C++ I am trying to make a simple program that allows a user to input a name followed by a score and then allows the user to enter a name and get the score that name was entered with. The program works fine until I enter an escape character (ctrl + z) once I'm done entering names, after entering the escape character the program will output the line "Enter name of student to look up the score" but not allow the user to input the name and instead reads out "Press any key to exit". I'm totally stumped on how to fix this and any help is greatly appreciated.
#include "stdafx.h"
#include <std_lib_facilities.h>
int main()
{
vector <string>names;
vector <int>scores;
string n = " "; // name
int s = 0; // score
string student = " ";
cout << "Enter the name followed by the score. (Ex. John 89)" << endl;
while(cin >> n >> s)
{
for(size_t i = 0; i < names.size(); ++i)
{
if(n == names[i])
{
cout << "Error: Duplicate name, Overwriting" << endl;
names.erase(names.begin() + i);
scores.erase(scores.begin() + i);
}
}
names.push_back(n);
scores.push_back(s);
}
cout << "Name: Score:" << endl;
for(size_t j = 0; j < names.size(); ++j)
{
cout << names[j];
cout <<" " << scores[j] << endl;
}
cout << "Enter name of student to look up their score" << endl;
cin >> student;
for(size_t g = 0; g < names.size(); ++g)
{
if(student == names[g])
{
cout << "Score: " << scores[g] << endl;
}
}
keep_window_open();
return 0;
}
After you press the CTRL+Z key combination, which induces an EOF state to the cin stream, you need to bring the cin input stream back to its normal 'good' state to be able to use it again.
Add the following code after your for loop where you print the contents of the vectors.
cin.clear();
You may also check the state of the standard input stream using the rdstate() function. Anything other than 0 means that the standard stream is in an error state.
As has been said, you need to clear the error state on std::cin after reading the records failed.
std::cin.clear();
should do the trick. Here's my take on this using
proper data structures instead of two isolated vectors
const correctness
separating functions
no more hacky .erase() calls with magic indexes
#include <map>
#include <iostream>
std::map<std::string, int> read_records()
{
std::map<std::string, int> records;
std::string name;
int score;
std::cout << "Enter the name followed by the score. (Ex. John 89)" << std::endl;
while(std::cin >> name >> score)
{
if (records.find(name) != end(records))
{
std::cout << "Error: Duplicate name, Overwriting" << std::endl;
} else
{
records.insert({name, score});
}
}
std::cin.clear();
return records;
}
int main()
{
auto const records = read_records();
std::cout << "Name\tScore:" << std::endl;
for(auto& r : records)
std::cout << r.first << "\t" << r.second << std::endl;
std::cout << "Enter name of student to look up their score: " << std::flush;
std::string name;
if (std::cin >> name)
{
std::cout << "\nScore: " << records.at(name) << std::endl;
}
}
If you required contiguous storage, use a flat_map like the one from boost.
#include <iostream>
#include <ctype.h>
using namespace std;
void isPalindrome();
int main()
{
char response;
isPalindrome();
cout << "Input another string(y/n)?" << endl;
cin >> response;
response = toupper(response);
if (response == 'Y')
isPalindrome();
return 0;
}
void isPalindrome()
{
char str[80], str2[80];
int strlength;
int j = 0;
int front, back;
bool flag = 1;
cout << "Input a string:" << endl;
cin.getline(str, 80);
strlength = strlen(str);
for (int i = 0; i < strlength; i++)
{
if (islower(str[i]))
str[i] = toupper(str[i]);
}
for (int i = 0; i < strlength; i++)
{
if (isalpha(str[i]))
{
str2[j] = str[i];
j++;
}
}
str2[j] = '\0';
front = 0;
back = strlength - 1;
for (int i = 0; i < j / 2; i++)
{
if (str2[front] != str2[back])
{
flag = 0;
break;
}
}
if (!(flag))
cout << "It is not a palindrome" << endl;
else
cout << "It's a palindrome" << endl;
cout << "str: " << str << " str2: " << str2 << " strlength: " << strlength << " j: " << j << endl;
cout << "front: " << front << " back: " << back << " flag: " << flag << endl;
}
I was just wondering if anybody could help explain to me why my code isn't working.
I can run it once just fine and I get the right answer, but when the prompt asks if I want to input another string and I type 'y', the prompt just skips over the input and terminates on it's own.
I tried cin.ginore('\n', 80), but that just gave me a bunch of blank lines. I added the bit of code at the end to check the values and they all go to 0 and drop the strings.
Maybe a link to a proper explanation of how the system handles memory?
edit:
I keep getting the same problem when running the input sequence a second time. The output looks like this:
Input a string:
Radar
It's a palindrome
Input another string(y/n)?
y
_ <- this being my cursor after pressing enter 3 times
I'll just re-build the program from scratch and try to do it without a function. I'd still appreciate a link to a page that explains how to process user input using modern c++.
The problem is with:
cin >> response;
This reads the user input y/n into the variable response but a newline is left in the input buffer which is picked by the getline function the isPalindrome function.
To fix this you need to remove the newline from the input buffer after you read the user response. You do it by using:
cin >> response;
std::cin.ignore(INT_MAX);
With the above fix you can retry the palindrome check just once. To make multiple retries possible you'll need a loop. I would recommend a do-while loop in your main as:
char response;
do {
isPalindrome();
cout << "Input another string(y/n)?" << endl;
cin >> response;
std::cin.ignore(INT_MAX);
response = toupper(response);
} while(response == 'Y');
You need a loop. There's no code that instructs the program to go back to the top.
char response = 'Y';
while (response == 'Y') {
isPalendrome();
cout << "Input another string(y/n)?" << endl;
cin >> response;
}
This isn't your entire program, just key elements that you need for a while loop. You should get an understanding of how while works and make this work for your program.
In contemporary C++ one would typically use standard library components for string processing:
#include <iostream>
#include <string>
int main()
{
std::string line1, line2, response;
do
{
std::cout << "First string: ";
if (!std::getline(std::cin, line1)) { /* error */ }
std::cout << "Second string: ";
if (!std::getline(std::cin, line2)) { /* error */ }
// check line1 and line2 for palindromy
std::cout << "Again (y/n)? ";
std::getline(std::cin, response);
} while (std::cin && (response == "y" || response == "Y"));
}