Reading and diaplaying a character array from a file in c++ - c++

I'm trying to write a program where a name and number of votes are read from a file. However I can't get the char array to read properly from the file.
void Output(char candidateLastName[][10], long votesRecieved[])
{
ifstream Electionr("Election.dat");
int loop = 0;
cout << left << setw(10) << "Candidate" << setw(5) << "Votes" << endl;
Electionr >> candidateLastName[0][10];
Electionr >> votesRecieved[0];
cout << setw(10) << candidateLastName[0] << setw(5)
<< votesRecieved[0] << endl;
for(int loop = 1; loop < 5; loop++)
{
Electionr >> candidateLastName[0][10];
Electionr >> votesRecieved[loop];
cout << setw(10) << candidateLastName << setw(5)
<< votesRecieved[loop] << endl;
}
Electionr.close();
}
While the numbers from the file read properly, the characters will not.

Electionr >> candidateLastName[0][10];
This is reading in a single character. Ignore for a moment that it's reading into the wrong location (the first character of the string at index 1)... I suspect that you want to do something like:
Electionr >> candidateLastName[0];
Also, I presume in your loop you want to use the loop variable instead of 0 to index the array. In that case, why did you not start your loop at zero and avoid duplicating that code?
for(int loop = 0; loop < 5; loop++)
{
memset( &candidateLastName[loop], 0, 10 );
Electionr >> candidateLastName[loop];
Electionr >> votesRecieved[loop];
cout << setw(10) << candidateLastName[loop] << setw(5)
<< votesRecieved[loop] << endl;
}
(I've also made a minor fix to the cout call above)
Be aware that you may need to explicitly null-terminate your strings (I forced that in my modified loop, but only if you happen to read 9 characters or less - otherwise you will overflow and have problems). I'm not sure if this is handled when reading into character arrays with the >> operator. I never do it for something like this. I use std::string instead.

Your first read should be:
Electionr >> candidateLastName[0];
And within your loop, you want:
Electionr >> candidateLastName[loop];
But, you are assuming each name will be at most 9 characters long (plus one for the null termination).
It would be safer to make your name array and array of std::string.
void Output(std::string candidateLastName[], long votesRecieved[])

First off you've got an out of bounds index on the array. Also, you're always writing to the first element. Also you shouldn't use raw arrays like that. IF you're going to use a fix sized array, at least use a std::array. But in this case std::string is most appropriate.
Anyway, it's hard to tell what you actually want, but here's my best guess at how to rewrite the code.
std::map<std::string, unsigned long> Output()
{
std::map<std::string, unsigned long> data;
ifstream Electionr("Election.dat");
cout << left << setw(10) << "Candidate" << setw(5) << "Votes" << endl;
while(Electionr.good()){
std::string name;
unsigned long votes = 0;
getline(Electionr, name, ' ');
Electionr >> votes;
data[name] = votes;
cout << setw(10) << name << setw(5)
<< votes << endl;
}
return data;
}

Related

Runtime error while using loop to pass info stream to setter

So, I've been practicing some basic file manipulation in Visual Studios 2017 using c++ and I'm getting an unexpected runtime error when I utilize either a while or for loop to pass the intermediary variables to the setter functions. First I tried a while loop with the runtime error, then I tried to type it out line for line and it worked just fine. Curious if it was just an issue with the while loop, I replicated it in a for loop and had the same runtime error. So I suppose my question is, why am I getting this error? I know that beggars can't be choosers, but I'm anxious to understand exactly why so detailed answers, as if explaining to an idiot, would be greatly appreciated. Thanks very much for taking time to answer my question in advance.
Below is my entire source file. I feel the need to elaborate. Below is a rudimentary attempt at taking a file with thirteen of the most popular full names in the United States and populating an array of classes with said information. I didn't include the class header and .cpp because it's pretty basic. There are only three private variables "firstName", "lastName", "numPeople". Then there's a variety of the expected getters and setters for said variables. I don't think that the class itself is a problem (outside of just poor execution due to ignorance) because when I didn't use a loop and called the class methods, it behaved just fine. Also, if you'll notice, there is both a while loop and a for loop, that have been commented out. I thought it'd be important for everyone to see just HOW I attempted to implement my loops in case of blatant logic errors I'm missing. Thanks again!
#include <fstream>
#include "listNames.h"
int main()
{
listNames fullNames[13];
std::string first;
std::string last;
int num = 0;
int i = 0;
std::ifstream nameFile("fullNames.txt");
std::cout << "FullName\tNumber of people\n";
nameFile >> first;
nameFile >> last;
nameFile >> num;
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople() <<
std::endl;
i++;
nameFile >> first;
nameFile >> last;
nameFile >> num;
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople() <<
std::endl;
/*for (i = 0; i < 13; i++)
{
nameFile >> first;
nameFile >> last;
nameFile >> num;
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
i++;
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople()
<< std::endl;
}*/
/*while (nameFile >> first >> last >> num)
{
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
i++;
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople()
<< std::endl;
if (i == 13)
{
break;
}
}*/
return 0;
}
The problem in the for loop:
You are incrementing i twice in each run of the loop. Remove the i++ line inside the loop.
It should be:
for (i = 0; i < 13; i++)
{
// Break of out the loop if there is an error in reading.
if ( !(nameFile >> first >> last >> num) )
{
break;
}
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
// This is the problem.
// i++;
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople()
<< std::endl;
}
The problem in the while loop:
You are incrementing i first before accessing the array while printing the contents. When i is equal to 12, you end up accessing the array using an out of bounds index, which caused undefined behavior.
It should be:
while ( nameFile >> first >> last >> num )
{
fullNames[i].set_firstName(first);
fullNames[i].set_lastName(last);
fullNames[i].set_numPeople(num);
std::cout << fullNames[i].get_firstName() << " " <<
fullNames[i].get_lastName() << "\t" << fullNames[i].get_numPeople()
<< std::endl;
// Increment it after printing.
i++;
if (i == 13)
{
break;
}
}

Storing user input data in a dynamic array of structures/ displaying said data. C++

So for my project I have to...
Create a structure using the format: struct PERSON{string name; int age; float gpa;};
Ask the user to enter the number of records they would like to enter.
Create a dynamic array p of PERSON type to be able to hold all records in part 2
Read data into array p in part 2
Display array p
However, whenever I run my program and begin to enter my data it won't let me enter more than one set of data and the output seems to make little sense.
Here is my code:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct PERSON
{
string name;
int age;
float gpa;
};
PERSON *p;
int main()
{
int count;
cout << "Please enter the number of records you wish to enter: ";
cin >> count;
p = new (nothrow) PERSON[count];
if (p == nullptr)
cout << "Error: memory could not be allocated";
for (int i = 0; i < count; i++)
{
cout << "Enter name, age, and gpa: ";
getline(cin, p[i].name);
cin >> p[i].age;
cin >> p[i].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw(8) << "NAME" << setw(7) << "AGE" << setw(7) << "GPA" << endl;
cout << "--------------------------" << endl;
for (int i = 0; i < count; i++)
{
cout << p[i].name << " " << p[i].age << " " << p[i].gpa << endl;
}
return 0;
}
Now, this format seemed to work just fine when I was copying data from a file into a dynamic array of structures, but I can't seem to get it to work now that I'm dealing with user input.
Here is what a trial run of the program produces:
Please enter the number of records you wish to enter: 3
Enter name, age, and gpa: Robert 21 2.1 // This is the info I tested
Enter name, age, and gpa:
Enter name, age, and gpa:
This is the content of array p:
NAME AGE GPA
--------------------------
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
-842150451 -4.31602e+008 //?
Press any key to continue . . .
I've probably gone over the code two dozen times now and have been searching for an answer for quite a while. Any advice/analysis/comments would be greatly appreciated.
So #AndyG suggested that I change the getline(cin, p[i].name) to cin >> p[i].name and that seemed to clear things up. Thank you!
Sorry to trouble everyone over such a silly muck-up.
The Problem
Your getline(cin, p[i].name) is actually grabbing all your input on the same line, and your later cin statements are going to be mismatched.
std::getline should be reserved for getting an entire line into a character buffer or std::string, i.e., it's not always the appropriate tool for reading in a string.
The Solution
Instead, you simply need to replace getline(cin, p[i].name) with cin >> p[i].name.
Live Demo
Other minor changes
Remove the nullptr check:
if (p == nullptr)
cout << "Error: memory could not be allocated";
Because in C++ you will get an exception thrown if the new operator fails to allocate. Like bku_drytt mentioned, you can wrap this in a try catch block instead, although I'd personally just remove it altogether.
#include <new>
// ...
Person* p = null;
try
{
p = new Person[count];
} catch(std::bad_alloc& ex)
{
cerr << "Unable to allocate memory for Person! Error msg: " << ex.what() << "\n";
return 1;
}
Finally, don't forget to delete[] the memory you allocated for p:
// ...
delete[] p;
return 0;
} //end of int main()
Even though your OS will most definitely clean that memory up for you, you should still ensure you clean it up manually. Good practice at least. Alternatively, you can use a std::array or a std::vector instead, which will still give you random access just like the c-style array of PERSON you already have.
The std::getline() function is "misbehaving" in relation to your intentions because there is (I think) a new line character left in the stream input queue.
You can discard such characters by using the following command cin.sync().
Why does this happen? Because operator>> does not read whitespaces, but std::getline() does, so when the cin >> count; statement is executed and you press enter, the new line character (from pressing enter) is left in the stream input queue, which is automatically read by std::getline() upon execution.
There are other functions that can accomplish this and perhaps someone more informed can tell us which is the best choice.
Here is a fixed version:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
struct Person
{
string name;
int age;
float gpa;
};
int main()
{
cout << "Please enter the number of records you wish to enter: ";
int count;
cin >> count;
Person* p = new Person[ count ];
for ( int i = 0; i < count; i++ )
{
cin.sync(); // added this to discard unused characters from input queue
cout << "Enter name: ";
getline( cin, p[ i ].name );
cout << "Enter age: ";
cin >> p[ i ].age;
cout << "Enter gpa: ";
cin >> p[ i ].gpa;
cout << endl;
}cout << endl;
cout << "This is the content of array p: " << endl;
cout << right << setw( 8 ) << "NAME" << setw( 7 ) << "AGE" << setw( 7 ) << "GPA" << endl;
cout << "--------------------------" << endl;
for ( int i = 0; i < count; i++ )
{
cout << p[ i ].name << " " << p[ i ].age << " " << p[ i ].gpa << endl;
}
return 0;
}
Notes:
You do not need to check if Person* p is nullptr because the new operator will throw a std::bad_alloc exception if it fails to allocate enough memory. Consider wrapping that memory allocation in a try catch block if you really want to.

C++ Vector troubles

Heres a function of my program, which keeps crashing. The purpose of the function is the search through the vectors and display what they rank and the frequency, which is read into thier respective vectors. Im guessing its my while loop but im not sure.
The vectors are correctly read in.
void searchNames(vector<string> girlsname,vector<int> girlsfeq,vector<string> boysname,vector<int> boysfeq){
string name1;
string name;
int SIZE = girlsname.size();
int SIZE2 = boysname.size();
while(name1 != "quit"){
int i=0;
int frank=find(girlsname.begin(), girlsname.end(), name1) - girlsname.begin();
int mrank=find(boysname.begin(), boysname.end(), name1) - boysname.begin();
name1.clear();
name.clear();
cout << "Please enter a name: ";
cin >> name1;
cout << endl;
while(i< SIZE && name1 !=girlsname[i++]){
frank++;
}
name.clear();
i=0;
while(i< SIZE2 && name1 !=boysname[i++]){
mrank++;
}
cout << left << setw(15) << "Name" << right << setw(10) << "Girl Rank" << right << setw(10) << "Frequency" << right << setw(10) << "Boy Rank"<< right << setw(10) << "Frequency" << endl;
if(frank != SIZE && mrank != SIZE2){
cout << left << setw(15) << name1 << right << setw(10) << frank << right << setw(10) << girlsfeq[frank] << right << setw(10) << mrank << right << setw(10) << boysfeq[mrank] << endl;
}
}
First, in any of your loops, you don't change the value of i, so they are potentially infinite.
Second, if I understand what you're trying to do, you have paired vectors and you search for an index on which the name is in the first vector and then use it to get the value from second vector. You can use std::find function for that:
int frank = find(girlsname.begin(), girlsname.end(), name1) - girlsname.begin();
Now, you can access the girlsfreq[frank] value. If the name is not present in the vector, the frank equals size of the vector, which you have to check for. Don't forget to add include:
#include <algorithm>
I am not able to reproduce your crash. Instead, I am able to reproduce an infinite loop. Taking a closer look to your while loops, it becomes evident: the value of i is never changing. If it cannot find a name match, it will keep incrementing the value of frank. So, a fix to this problem would be:
while(i < SIZE && name1 != name) {
name = girlsname[i];
frank++;
i++;
}
But it can be improved: you don't need that assignment. Also, you can use the advantage of the postfix increment:
while(i < SIZE && name1 != girlsname[i++]){
frank++;
}
Doing this, you also remove the necessity of name and name1.

Getting stuck in infinite loop

When I'm running this program for class, I'm getting stuck in an infinite loop whenever I enter a '|' character to end the while loop. I feel like I'm missing something obvious.
This problem is found on page 126 of Bjarne Stroustrup's C++ Programming book, but as a quick rundown, I'm just supposed to find the largest and smallest numbers that are typed in the by the user and return info on it. Typing in '|' is supposed to exit the loop so that I can get down to the part where it gives information about all the numbers inputted, but whenever I type that character (or any character that's not a number), it creates an infinite loop.
Here is my code.
int main()
{
vector<double> nums;
while (true)
{
double current_num;
cout << "enter a double \n";
cin >> current_num;
if (current_num == '|')
break;
nums.push_back(current_num);
sort(nums.begin(), nums.end());
cout << nums[nums.size()-1] << " is the largest so far.\n";
cout << nums[0] << " is the smallest so far.\n";
}
cout << nums[nums.size()-1] << " is the largest number.\n";
cout << nums[0] << " is the smallest number.\n";
cout << "Number of values entered: " << nums.size() << '\n';
double sum = 0;
for (int k = 0; k<nums.size(); ++k)
sum += nums[0];
cout << "Sum of all values: " << sum << '\n';
for (int j=0; j<nums.size(); ++j)
cout << nums[j] << ' ';
return 0;
}
I was using VS13 in class and I wasn't having this problem, but now I'm coding in notepad++ and using PuTTY to compile at home (although I doubt this has anything to do with it).
You are comparing a character with a double here :
if (current_num == '|')
And this comparison will never do what you want it to do.
Read a character first, compare it with '|', then do a double conversion if necessary.
Note:
For your record the ASCII value of '|' is 124, so if you enter 124 your loop will end...
Because you are trying to insert a non number into a double.
I thing that in your case you should read the input into a string/char and parse it.
you are comparing number with a character.
if (current_num == '|')
current_num contains the number in type double that you are trying to compare with a char which is '|'
Problem is here:
if(current_num == '|'){
}
Instead read in a std::string and parse it to a double.
So the modified snippet will look something like this:
while (true)
{
string strNum;
double current_num;
cout << "enter a double \n";
cin >> strNum;
if (strNum == "|")
break;
istringstream strm(strNum);
strm >> current_num;
nums.push_back(current_num);
sort(nums.begin(), nums.end());
cout << nums[nums.size()-1] << " is the largest so far.\n";
cout << nums[0] << " is the smallest so far.\n";
}

Sudoku game design problems C++

I have (yet another) question about chars. Thanks to those who helped me with this before. I'm trying to do mainly 4 things at this point in the program. That is:
Build a 2D array 9x9 and fill it with underscores.
Ask for a row/column and then the number that the user wishes to go into that row/column as many times as the user wants.
Replace the specified blanks with the specified numbers.
Output the entire 9x9 char array on an ASCII art Sudoku board.
(Solving will come later.)
My problem is that when I enter the row/column and the number that I want to go into that row/column the dash that was originally in that spot disappears, but the number I entered does not appear in its place.
Here is the code so far:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main () {
//Builds 9x9 char array.
char dash[9][9];
for (int array=0; array<9; array++) {
for (int array2=0; array2<9; array2++) {
dash[array][array2]='_';
}
}
cout << "Input the row #, then the column #, then the number that you wish to fill that spot." << endl;
cout << "Remember that a Sudoku board is 9x9." << endl;
cout << "When you wish to finish input and solve, type all 0's and press enter." << endl;
int rowb;
char row[99];
int columnb;
char column[99];
int numb;
char num[99];
//Inputs the row/column and number to go into specified row/column.
int control=0;
while (rowb!=0){
control++;
cout << "Row: ";
cin >> rowb;
cout << "Column: ";
cin >> columnb;
cout << "Number: ";
cin >> numb;
row[control]=rowb-1;
column[control]=columnb-1;
num[control]=numb;
}
int length;
length=strlen(row);
//Replaces the _'s in the specified rows/columns and replaces them with the integer the user specified. This is where I think I'm having the problem.
for (control=0; control<length; control++) {
dash[row[control]][column[control]]=num[control];
}
//Builds the Sudoko board and outputs the full 9x9 array.
cout << "╔═══════════╦═══════════╦═══════════╗" << endl;
for (int count=0; count<3; count++) {
for (int count2=0; count2<3; count2++) {
cout << "║_" << dash[count][count2*3] << "_|_" << dash[count][count2*3+1] << "_|_" << dash[count][count2*3+2] << "_";
}
cout << "║" << endl;
}
cout << "╠═══════════╬═══════════╬═══════════╣" << endl;
for (int count=3; count<6; count++) {
for (int count2=0; count2<3; count2++) {
cout << "║_" << dash[count][count2*3] << "_|_" << dash[count][count2*3+1] << "_|_" << dash[count][count2*3+2] << "_";
}
cout << "║" << endl;
}
cout << "╠═══════════╬═══════════╬═══════════╣" << endl;
for (int count=6; count<9; count++) {
for (int count2=0; count2<3; count2++) {
cout << "║_" << dash[count][count2*3] << "_|_" << dash[count][count2*3+1] << "_|_" << dash[count][count2*3+2] << "_";
}
cout << "║" << endl;
}
cout << "╚═══════════╩═══════════╩═══════════╝" << endl;
return 0;
}
There is a problem assignment of the number entered in the loop.
//Replaces the _'s in the specified rows/columns and replaces them with the integer the user specified. This is where I think I'm having the problem.
for (control=0; control<length; control++) {
dash[row[control]][column[control]]=num[control]; //<<<--- Assignment issue.
}
You are assigning an integer value in a character array & thus when you display you will get the corresponding char for the ascii value & not the integer. Try changing the assignment as follows:
//Replaces the _'s in the specified rows/columns and replaces them with the integer the user specified. This is where I think I'm having the problem.
for (control=0; control<length; control++) {
dash[row[control]][column[control]]=num[control] + '0'; // Convert to ascii value of the integer, but will fail if not b/w 0 & 9.
}
Checking if the number entered in is between 1 & 9 is also advised if you choose to use the above observation.
Please add checks for the row & column entered as enter values which are not b/w 1 & 9 will lead to undefined behaviour due to accessing out of bound array elements if the values entered are not b/w 1 & 9.
Also as mentioned by Benjamin Lindley please update strlen code.
Hope this helps!
length=strlen(row);
This is undefined behavior, because row[0] was never initialized, and you never null terminate the string.
char row[99];
...
int control=0;
while (rowb!=0){
control++;
...
row[control]=rowb-1;
...
Notice that the first time through the loop, control is 1. So, you're setting the value of row[1], but not row[0]. Move the increment to the end of the loop. There may be some other problems, but this is the primary one responsible for the behavior you're seeing.
Also, for strlen to work, you need to null terminate the string.
And finally, you're making the same mistake you've made in this question and this question. Why aren't you seeming to get that? Chars display differently than ints. The following code will not display the number 1:
char c = 1;
std::cout << c;
Look at the answers to those other two questions.