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;
}
}
Related
I try this code using a method, array, and struct, but it doesn't produce output at all.
I also use most types of include, but it still doesn't produce any output.
#include<conio.h>
#include<iostream>
#include<stream>
#include<string>
#include<stream>
using namespace std;
struct PetLabel {
string petname;
string pettype;
int petprice;
string petowner;
string phoneno;
};
PetLabel pet [10];
stringstream ss;
void DataPet();
void petPrice();
void findOwner();
int main(){
DataPet();
findOwner();
petPrice();
return 0;
}
void DataPet(){
ifstream infile;
infile.open("petSold.txt");
string cnvt[10];
for(int i = 0; i < 10; i++){
getline(infile, pet[i].petname, ',');
getline(infile, pet[i].pettype, ',');
getline(infile, cnvt[i], ',');
ss << cnvt[i];
ss >> pet[i].petprice;
getline(infile, pet[i].petowner, ',');
getline(infile, pet[i].phoneno);
ss.clear();
}
infile.close();
}
void findOwner(){
int chosen;
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
i = chosen;
}
}
cout << "Owner name : " << pet[chosen].petowner << " Phone no : " << pet[chosen].phoneno << endl;
}
void petPrice(){
ofstream outfile("catSold.txt");
outfile << "The cat sold with price greater than RM1000" << endl;
for (int i = 0; i < 10; i++){
if (pet[i].petprice > 1000){
cout << pet[i].petname << "," << pet[i].pettype << "," << pet[i].petprice << "," << pet[i].petowner << "," << pet[i].phoneno << endl;
}
}
outfile.close();
}
the output that I get is:
Owner name : Phone no :
but I can't understand, because there is no syntax error at all.
Is there anything I can add, so I can get the right output?
This method is weird:
void findOwner(){
int chosen;
for (int i=0;i<10;i++){
if (pet[i].petname == "Uyasolu"){
i = chosen;
}
}
cout<<"Owner name : "<<pet[chosen].petowner<<" Phone no : "<<pet[chosen].phoneno<<endl;
}
I think you mean chosen = i instead.
findOwner() is coded wrong.
int chosen; is uninitialized before the loop is entered. Inside the loop, chosen is never updated (i is updated instead, which affects the looping). After the loop is done, chosen has an indeterminate value, so accessing pet[chosen] is undefined behavior.
It should look more like this instead:
void findOwner(){
int chosen = -1;
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
chosen = i;
break;
}
}
if (chosen != -1)
cout << "Owner name : " << pet[chosen].petowner << " Phone no : " << pet[chosen].phoneno << endl;
else
cout << "Pet not found" << endl;
}
Alternatively:
void findOwner(){
for (int i = 0; i < 10; i++){
if (pet[i].petname == "Uyasolu"){
cout << "Owner name : " << pet[i].petowner << " Phone no : " << pet[i].phoneno << endl;
return;
}
}
cout << "Pet not found" << endl;
}
There are some other issues in your code, too:
don't use <conio.h>, its old, and you are not using anything from it anyway.
you have #include<stream> twice, and also <stream> should be <sstream>
string cnvt[10]; should be string cnvt; and then cnvt[i] should be cnvt. You don't need a whole array when you are converting only 1 string at a time.
ss.clear(); does not do what you think it does. You need to use ss.str("") instead. Or, simply move the declaration of ss inside the loop as a local variable so it is destroyed and recreated on each loop iteration.
petPrice() is creating an output catSold.txt file, but is not writing anything to it.
I keep gettin this error on my code. My program was running perfectly just a few minutes ago and all of a sudden it kept throwing this error at me. I tried to find the error by using cout << "here" << endl in between some lines and sometimes it usually stops within my for loop inside the readInName() functions. Sometimes it would fully run and print out everything even the "here". Other times i would just get the same error.
#include <fstream>
#include <string>
using namespace std;
// global variables
// these must be changed depending on the file.
const int SIZE = 10; // num of students
const int AMOUNTOFGRADES = 5; // num of tests per student
ifstream input;
// function declarations
void readInName();
float calculateAvgAndGrade(int count);
void header();
int readInGrades(int);
int main(){
header();
readInName();
return 0;
}
void readInName(){
string name[SIZE] = {""};
input.open("grade.txt");
int row,column;
int count;
for(row = 0; row < SIZE; row++){
input >> name[row];
cout << setw(10) << name[row] << ": ";
count = readInGrades(row);
cout << setw(5) << calculateAvgAndGrade(count) << endl;
}
input.close();
}
int readInGrades(int){
int r,c;
int grades[SIZE][AMOUNTOFGRADES] = {0};
int count = 0;
for(c = 0; c < AMOUNTOFGRADES; c++){
input >> grades[r][c];
cout << setw(5) << grades[r][c] << " ";
count = count + grades[r][c];
}
return count;
}
float calculateAvgAndGrade(int count){
return float(count)/AMOUNTOFGRADES;
}
void header(){
cout << setw(15) << " Names " << setw(20) << "Grades " << setw(18) << "Avg " << endl;
cout << setfill('-') << setw(53) << '-' << endl << setfill(' ');
}
In readInGrades(), r is used uninitialized as an index to write into grades[r][c], which is undefined behavior. So r may by chance have any value and write to arbitrary locations in memory. This would sometimes corrupt the stack and trigger a segmentation fault. A helpful tool to troubleshoot these kinds of errors is AddressSanitizer, enabled in clang or gcc by compiling with -fsanitize=address, or if you are using Xcode, there is an option for it. Another great tool is valgrind.
In my opinion:
input >> grades[r][c];
r - isn't initialized.
I'm trying to swap two array elements that are initialized strings (see code below), I thought i knew how to do it but the way I tried doesn't seem to be working. We're not allowed to use the function "swap" that I've seen on many other forum sights with a similar question as mine. Therefore I've used a temporary index variable to swap them but that doesn't seem to be working. Not sure how to fix it and make it work, so my question is how do I do it.
I'm pretty new to programming so the answer may not be as evident to me yet. I've been staring at this for a while and still can't see it, I also tried asking on reddit but they didn't give me a very concise or helpful answer. If you could help me out as to why it wont swap the elements that would be great and if you see any other bugs or improvements I could make please let me know, your feedback is greatly appreciated, thank you!
Code:
#include <iostream>
using namespace std;
void printArray(string names[]) {
for (int i = 0; i < 7; i++) {
cout << names[i] << " ";
}
}
int main() {
int x = 0, a, b;
string answer, name1, name2;
string index;
string names[7] = {"John", "Dave", "Jim", "Amanda", "Kelsey", "Don", "Jennifer"};
printArray(names);
while (x < 1) {
cout << endl << "Do you want to swap students? " << endl;
cin >> answer;
if (answer == "Yes" || answer == "yes") {
cout << "Who would you like to swap?" << endl;
cin >> name1;
for(a = 0; a < 7; a++) {
if (names[a] == name1) {
cout << "Yes, " << name1 << " is in the line." << endl;
}
}
cout << "Who would you like to swap " << name1 << " for?" << endl;
cin >> name2;
for(b = 0; b < 7; b++) {
if (names[b] == name2) {
cout << "Yes, " << name2 << " is in the line!!" << endl;
index = names[a];
names[a] = names[b];
names[b] = index;
printArray(names);
}
}
} else {
cout << endl << "Thanks, please behave now, students!" << endl;
x++;
}
}
return 0;
}
More context:
Print out the current class line-up (i.e., the current contents of the array, in order).
Present the user with a prompt asking if they would like to swap 2 students.
If they say yes, proceed to step 3.
Ask the user for the names of the two students to be swapped.
If the two students are both in the array, swap their positions in the array. If either student is not in the class, print an error message on the console, e.g. "Sorry, you have to pick 2 students who are in the line!".
No matter the outcome of step 4, return to step 1.
While searching for name1 ,the loop runs till 7 no matter if its found or not which messes with your swapping task:
index = names[a]; //a=7
names[a] = names[b]; //a=7
names[b] = index;.
Use break:
if (names[a] == name1) {
cout << "Yes, " << name1 << " is in the line." << endl;
break;
}
The value of a is always 7 while swapping is being done! So it is not working.
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.
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;
}