How does one correctly pass on ofstream object? - c++

I am writing code to have input student and grade objects, sort them, calculate an average gpa, and output them to a file. My current problem is that the output of my student objects are given after I sort them and print them, but the code does not seem to leave the loop once I do that. I have a print function in which I am passing an ofstream. From debugging, I know that the out ofstream object reads everything, but then it seems to freeze, and not output anything else into it. Can somebody tell me if I passed by reference incorrectly, or if I need to do something else? The error where the compiler didn't keep going through is marked in the code.
Sorry, so for more information, below is a snip it of what is run
output code in text file
Notice how "got through it" is only displayed once. I know that it's not an issue opening the file and outputting, because it actually did output the right stuff. Also, for correction, the compiler actually compiles everything fine. It is at runtime where the terminal stalls and nothing more is output.
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <string>
#include "Grade.h"
#include "Student.h"
using namespace std;
const double A_NUM = 4.0, B_NUM = 3.0, C_NUM = 2.0, D_NUM = 1.0, E_NUM = 0.0, B_PLUSSNUM = 3.4, C_PLUSSNUM = 2.4, D_PLUSSNUM = 1.4;
const double A_MINUSNUM = 3.7, B_MINUSNUM = 2.7, C_MINUSNUM = 1.7, D_MINUSNUM = 0.7;
const int FIRST_INDEX= 0;
const int START_NUM = 0;
const int LESS_ONE = 1, ABOVE_ONE = 1;
const int START_GRADE = 0;
void calcgrades(vector<Grade>& grades)//function that calculates gpa based on letter grades
{
const string A = "A", A_MINUS = "A-", B_PLUSS = "B+", B = "B", B_MINUS = "B-", C_PLUSS = "C+", C = "C", C_MINUS = "C-", D_PLUSS = "D+", D = "D", D_MINUS = "D-", E = "E";
int counter = START_NUM;//used to keep track of current student and current total grade
double current_grade = START_NUM;
for(int i = 0; i < grades.size();i++)
{
//while loop to get the student's current total grade if the next student id is different than the first one.
while(i < grades.size()-LESS_ONE && grades[i].getid() == grades[i+ABOVE_ONE].getid())
{
if (grades[i].getgrade() == A)
{
current_grade == A_NUM + current_grade;
}
if (grades[i].getgrade() == B)
{
current_grade == B_NUM + current_grade;
}
if (grades[i].getgrade() == C)
{
current_grade == C_NUM + current_grade;
}
if (grades[i].getgrade() == D)
{
current_grade == D_NUM + current_grade;
}
if (grades[i].getgrade() == E)
{
current_grade == E_NUM + current_grade;
}
if (grades[i].getgrade() == B_PLUSS)
{
current_grade == B_PLUSSNUM + current_grade;
}
if (grades[i].getgrade() == C_PLUSS)
{
current_grade == C_PLUSSNUM + current_grade;
}
if (grades[i].getgrade() == D_PLUSS)
{
current_grade == D_PLUSSNUM + current_grade;
}
if (grades[i].getgrade() == A_MINUS)
{
current_grade == A_MINUSNUM + current_grade;
}
if (grades[i].getgrade() == B_MINUS)
{
current_grade = B_MINUSNUM + current_grade;
}
if (grades[i].getgrade() == C_MINUS)
{
current_grade = C_MINUSNUM + current_grade;
}
if (grades[i].getgrade() == D_MINUS)
{
current_grade = D_MINUSNUM + current_grade;
}
}
if (grades[i+ABOVE_ONE].getid() != grades[i].getid() || i == grades.size()-LESS_ONE)
{
//computes the average if the currentid is not equal to the nextid
double avggpa = current_grade/counter;
grades[i].newgpa(avggpa);
counter = START_NUM;//resets counter for a new student to get his gpa
}
counter++;
}
}
int main(int argc, char* argv[])
{
string student_file, grades_file, idnum, name, address, pnum, course, gletter;
//creates student and grade objects from respective classes
vector<Student> students;
vector<Grade> grades;
student_file = argv[1];
//reads in information from file to import into student objects
ifstream sfile;
sfile.open(student_file);
while(getline(sfile, idnum))//gets the student information from student file
{
getline(sfile, name);
getline(sfile, address);
getline(sfile, pnum);
//creates student objects to import info. into, and inserts into students vector
Student s(idnum, name, address, pnum);
students.push_back(s);
}
sfile.close();
//opens information from the grade file
grades_file = argv[2];
//reads from file to import into grades objects
ifstream gfile;
gfile.open(grades_file);
//gets the grade information from the grades file
while(getline(gfile, course))
{
getline(gfile, idnum);
getline(gfile, gletter);
//creates grade objects to import info, and inserts into grades vector
Grade g(course, idnum, gletter, START_GRADE);
grades.push_back(g);
}
gfile.close();
//reads the query file
string query_file;
query_file = argv[3];
ifstream qfile;
qfile.open(query_file);
//reads from query file
//creates vector to store each student number
vector<string> query_nums;
string incheck;
while(getline(qfile, incheck))
{
query_nums.push_back(incheck);
}
qfile.close();
//sorts the information for the students
sort(students.begin(), students.end());
//sorts the information for the grades
sort(grades.begin(), grades.end());
ofstream outtxt;
string out_file = argv[4];
outtxt.open(out_file);
//outputs the student information, sorted now.
for(int i = 0; i < students.size();i++)
{
students[i].print(outtxt);
outtxt << "got through it";
}
//compiler did not get past here!
outtxt << "We're here!";
//outputs the grades with student id's, now sorted
for (int i = 0; i < grades.size(); i++)
{
grades[i].print(outtxt);
outtxt << "\n\n" << "some output!";
}
//calculates the average gpa for every student
calcgrades(grades);
for(int i = 0; i < query_nums.size(); i++)//goes through each query number to check first
{
for (int j = 0; j < students.size(); j++)//after, goes through each student object
{
if (query_nums[i] == students[j].getid())
{
//finds the gpa in the grades class that matches the student id
for (int k = 0; k < grades.size(); k++)
{
if (grades[k].getid() == query_nums[i])//
{
//outputs the resulting id, avg gpa, and name of matching students
outtxt << grades[i].getid() << "\tthere is nothinghere" << grades[i].getgpa() << "\t" << students[i].getname();
}
}
}
}
}
outtxt.close();
return 0;
}
student class with print function
class Student
{
private:
string idnum;
string name;
string address;
string pnum;
public:
//defines constructor
Student(string idnum,string name,string address,string pnum);
//prints each student info
void print(ofstream& out);
string getname();
string getid();
bool operator < (Student s) const {
return idnum < s.idnum;
}
};
student cpp file
#include "Student.h"
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;
//Student constructor, used to make student objects
Student::Student(string id,string nm,string add,string pnumber)
{
idnum = id;
name = nm;
address = add;
pnum = pnumber;
}
void Student::print(ofstream& out)
{
out << name << endl << idnum << endl << pnum << endl << address << endl;
}
string Student::getname()
{
//returns the student name
return name;
}
string Student::getid()
{
return idnum;
}

In answer your question regarding how to correctly pass an ofstream object, you are passing it correctly.
So why doesn't it work? From what is available to observe from your code, it should work just fine. I would suggest debugging the output file name in these lines of code:
ofstream outtxt;
string out_file = argv[4];
outtxt.open(out_file);
Verify that the open function succeeded by checking the ios::fail flag after calling the open function.
Try something like this after opening:
if (ios::fail)
cout << "fail";
I hope this helps.
Edited: There may be other problems, but the calcgrades() function has an access violation on the last for loop iteration, after the while loop, at this if statement:
if (grades[i+ABOVE_ONE].getid() != grades[i].getid() || i == grades.size()-LESS_ONE)

Related

Why do I get a segmentation fault when fetching this variable?

I am pulling names as strings from a file, create a Person *p object, and put it into an array.
Then to search the array for a name but when I try to fetch the name I get a segmentation fault.
Why is this segmentation fault happening, and what can I do to fix it?
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string firstName;
string lastName;
string phoneNumber;
public:
Person();
Person(string f, string l, string n);
~Person(void);
void setName()
{
}
string getFirstName()
{
return firstName;
}
string getLastName()
{
return lastName;
}
string getNumber() { return phoneNumber; }
void print();
};
Array creation.
{
ifstream file;
file.open("phonebook.txt");
if (!file.is_open()) //Check for File Error.
{
cerr << "Failed to open file" << endl;
exit(1);
}
//Get Array Size
string line;
while (getline(file, line))
{
count++;
}
file.close();
//Create an array
Person *arrList[count];
buildArray(arrList, count);
if (uInput == "a" || uInput == "A") //To add
{
int x = addPerson();
if (x == 1)
{
count++;
}
delete[] arrList;
}
void buildArray(Person *arr[], int size)
{
string f, l, n;
ifstream file;
file.open("phonebook.txt");
for (int i = 0; i < size; i++)
{
file >> f >> l >> n;
Person *p = new Person(f, l, n);
arr[i] = p;
delete p;
}
}
The search, This is the part that has the trouble. I have tried a few different things including creating 2 Persons, and comparing their parts but whenever it goes into the .h it can not return the name.
if (uInput == "s" || uInput == "S")
{ //To Search
string f, l;
cout << "Find Who (Firstname Lastname) " << endl;
cin >> f >> l;
Person *n = new Person(f, l, "");
int i = 0;
bool found = false;
while (i <= count && found == false)
{
Person *p = new Person("", "", "");
p = arrList[i];
if (p->getFirstName() == n->getFirstName() && p->getLastName() == n->getLastName())
{
arrList[i]->print();
found = true;
delete p;
delete n;
}
i++;
}
while (i == count && found == false)
{
cout << "No results found. " << endl;
found = true;
}
}

writing file in header c++

I have 3 files, book.h, book.cpp, bookdriver.cpp. I want to know if the ISBNs in a list are found in the array using binary search.
book.h:
using namespace std;
ofstream fout("output2.txt");
class Book
{
string author; // A string for the name of the author
string title; //A string for the book title
int ISBN; //A long integer for the ISBN
public:
Book(); //A default constructor
void print();
int getISBN() const; //A const function GetISBN that returns the integer containing the ISBN.
int binary_search(Book, int, int, int);
};
book.cpp- also includes print function which uses fout
#include "book.h"
//iterative binary search function, returns location of ISBN in array if present
int Book::binary_search(Book arr[], int x, int n, int ISBN)
{
while (n >= x)
{
int midpt = x + (n - x) / 2;
//if ISBN is in midpoint
if (arr[midpt].getISBN() == ISBN)
{
return midpt;
}
//if ISBN is greater than midpoint, ignore left side of array
if (arr[midpt].getISBN() < ISBN)
{
x = midpt + 1;
}
//if ISBN is smaller, ignore right side of array
else
{
n = midpt - 1;
}
}
//if ISBN not present
return -1;
}
bookdriver.cpp
#include "book.h"
const int num = 10; //number of book objects the array should hold *can be changed*
int main()
{
Book book_array[num] = {}; //array can hold num book objects
for (int c = 0; c < num; c++)
{
book_array[c] = Book();
book_array[c].getData(data); //reading book information
}
//read file
ifstream fin("bookISBN.txt");
int find_ISBN;
while (fin >> find_ISBN)
{
bool match = false;
int count = 0;
int result = binary_search(book_array[10], 0, num - 1, find_ISBN); //error here
if (result == -1) //if ISBN not found
{
fout << "Sorry, the ISBN " << find_ISBN << " is not found." << endl;
}
else
{
fout << "The ISBN " << find_ISBN << " is found in the system!" << endl;
}
count++;
}
return 0;
}
I'm using fout in both book.cpp and bookdriver.cpp so I have ofstream fout (output2.txt) in the header but I'm getting linker errors(Error LNK2005) in vs.
I think because of the one definition rule, fout is defined twice?
Here's a start:
#include <iostream>
#include <fstream>
using namespace std;
const int num = 2;
class Book
{
public:
Book(){};
string author;
string title;
int ISBN = 0;
int data;
};
int binary_search (Book arr[num], int x, int n, int ISBN, int overflow)
{
int mid = (x + n) / 2;
if (x == mid || n == mid)
overflow++;
if (overflow > 100)
return false;
if (arr[mid].ISBN > ISBN)
return binary_search(arr, x , mid, ISBN, overflow);
else if (arr[mid].ISBN < ISBN)
return binary_search(arr, mid, n , ISBN, overflow);
return true;
}
int main() {
ofstream fout("output2.txt");
ifstream fin("bookISBN.txt");
int find_ISBN;
Book book1;
book1.title = "Alice in Wonderland";
book1.author = "C.S. Lewis";
book1.ISBN = 1;
Book book2;
book2.title = "Wuthering Heights";
book2.author = "Emily Bronte";
book2.ISBN = 2;
Book book3;
book3.title = "Moby Dick";
book3.author = "Herman Melville";
book3.ISBN = 25;
Book book_array[num] = {book1, book2};
while (fin >> find_ISBN)
{
int result = binary_search(book_array, 0, num, find_ISBN, 0);
if (result == false)
{
fout << "Sorry, the ISBN " << find_ISBN << " is not found." << endl;
}
else
{
fout << "The ISBN " << find_ISBN << " is found in the system!" << endl;
}
}
fin.close();
fout.close();
return 1;
}

Copying Array - Crashes After Doubling Array

I'm having some trouble with this program, I'm aware I can use a vector but I'm trying to do it only using arrays. Once the program gets to the initial array size of 1000, it should double the array (in this case to 2000) after copying the data. So for example if I had a list of 3000 names, it'd double once at 1000, then again at 2000 - making the total list 4000. I'm not entirely sure why it crashes after I double the array size. Can someone help me with this?
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
struct Information {
char functionality;
string SSN;
string name;
};
Information* person;
int numPeople = 1000;
int numRetrieved = 0;
int numArray = 0;
int numInserted = 0;
int numDeleted = 0;
void doubleArray(Information *person){
numPeople = numPeople * 2;
Information* temp = new Information[numPeople];
memcpy(temp, person, numPeople/2);
delete[] person;
person = temp;
cout << "Person 1: " << person[0].name << " " << person[0].SSN << endl;
}
//Currently not using this until I figure out the double...
void halfArray(Information *person){
numPeople = numPeople / 2;
}
void deleteInfo(Information *person, string SSN){
for(int i = 0; i < numArray; i++){
if(person[i].SSN == SSN){
for(int k = i; k < numArray-1; k++){
person[k].SSN = person[k+1].SSN;
person[k].name = person[k+1].name;
}
numArray--;
numDeleted++;
if((numArray+1) < (numPeople / 4)){
//halfArray(person);
}
}
}
}
void retrieve(Information *person, string findSSN, int lastPerson){
for(int i = 0; i < lastPerson; i++){
if(person[i].SSN == findSSN){
numRetrieved++;
}
}
}
void insert(Information *person, string SSN, string name){
if(numArray == (numPeople - 1)){
doubleArray(person);
}
bool dontInsert = false;
for(int i = 0; i <= numArray; i++){
if(person[i].SSN == SSN){
dontInsert = true;
}
}
if(dontInsert){
dontInsert = false;
}else{
person[numArray].SSN = SSN;
person[numArray].name = name;
numArray++;
numInserted++;
}
}
int main(int argc, char* argv[]) {
person = new Information[numPeople];
char firstLetter;
string SSN, firstName, lastName, name;
fstream input(argv[1]);
for(int i = 0; !input.eof(); i++){
input >> firstLetter >> SSN >> firstName >> lastName;
name = firstName + " " + lastName;
switch(firstLetter){
case 'd':{
deleteInfo(person, SSN);
break;
}
case 'i':{
insert(person, SSN, name);
break;
}
case 'r':{
retrieve(person, SSN, numArray);
break;
}
}
}
input.close();
}
You delete the memory that the Information* points at, but you have a copy of that pointer, and assigning it to your temp variable has no effect outside of doubleArray.
Let's say that the value of person passed in is 0xC001C001;
void doubleArray(Information *person){
numPeople = numPeople * 2;
Information* temp = new Information[numPeople];
memcpy(temp, person, numPeople/2);
delete[] person;
person = temp;
cout << "Person 1: " << person[0].name << " " << person[0].SSN << endl;
}
You create a new pointer temp which has a value of 0xBAD1BAD1. You delete person (0xC001C001), then assign 0xBAD1BAD1 to person. Execution then continues in the insert function ...
void insert(Information *person, string SSN, string name){
if(numArray == (numPeople - 1)){
doubleArray(person);
}
bool dontInsert = false;
for(int i = 0; i <= numArray; i++){
if(person[i].SSN == SSN){
dontInsert = true;
}
}
The person variable here still has the value 0xC001C001, which has been deallocated. Your program blows up when you deference person with person[i].SSN.
When you want to change a pointer, the classic pattern is to pass a pointer pointer. (Two star programming)
void redo(P** p)
{
P* temp = new P();
delete *p;
*p = temp;
}

Program crashes due to undefined behavior

So I'm writing a program that creates a library for a collection of CDs and displays them. My program compiles but crashes whenever I write an array of pointers to songs from a file into structs contained within an array shown here:
//Get song array
for (int a = 0; a < num_songs; a++)
{
getline (infile, line);
sub = line.c_str();
word = createString(sub);
length = substr(word, -1, 5);
title = substr(word, 5, strlen(sub));
cd->song_array[a] = createSong(title,length);
destroyString(word);
}
I think it's due to undefined behavior, here's the .cpp file that this is happening in.
#include <iostream>
#include "CDs.h"
#include "CD.h"
#include <fstream>
#include <string>
#include <cstring>
using namespace std;
//Creates a collection of CDs
CDs* createCDs(const char* file_name)
{
//Declare variables and allocate memory
int max_cds = 50;
CDs* collection = new CDs;
collection->max_cds = max_cds;
CD** cd_array = new CD*[max_cds];
int num;
int sentinel = 0;
String* word;
string line;
CD* cd;
const char* sub;
String* length;
String* title;
//Open .txt file
ifstream infile;
infile.open(file_name);
if (infile.is_open())
{
while (infile.good())
{
for (int i = 0; i < max_cds; i++)
{
//Get the artist from .txt file
cd = cd_array[i];
getline (infile, line);
sub = line.c_str();
word = createString(sub); //Create string from infile line
cd->artist = word;
destroyString(word);
//Get the Title of the album from file
getline (infile, line);
sub = line.c_str();
word = createString(sub);
cd->title = word;
destroyString(word);
//Get the Year of the album from file
infile >> num;
cd->year = num;
//Get the Rating
infile >> num;
cd->rating = num;
//Get number of tracks
int num_songs;
infile >> cd->num_tracks;
//Get song array
for (int a = 0; a < num_songs; a++)
{
getline (infile, line);
sub = line.c_str();
word = createString(sub);
cout << "SHIT" << endl;
length = substr(word, -1, 5);
title = substr(word, 5, strlen(sub));
cd->song_array[a] = createSong(title,length);
destroyString(word);
}
cd_array[i] = cd;
sentinel++;
}
}
}
else
{
cout << "file did not open";
}
collection->cd_array = cd_array;
collection->num_cds = sentinel;
collection->max_cds = max_cds;
return collection;
}
I have no idea what to do to make this run, If someone could help that would be amazing.
edit - I didn't give the .cpp that is included and has some of the functions used
#include <iostream>
#include <cstring>
#include "String.h"
using namespace std;
//Function that creates a string
String* createString(const char* char_array)
{
//Allocate memory for a pointer to String struct
//String* string;
String* string = new String;
//Write the char_array to String struct
int length = strlen(char_array);
char array[30];
for (int i = 0; i <= length; i++)
{
array[i] = char_array[i];
string->array[i] = array[i];
}
return string;
}
//Function that displays the string
void displayString(String* str)
{
for (int i = 0; i < strlen(str->array); i++)
{
cout << str->array[i];
}
cout << endl;
}
//Function that destroys the string
void destroyString(String* str)
{
delete str;
str = NULL;
}
int find(String* str, char delimiter, int start)
{
for (int i = start; i <= strlen(str->array); i++)
{
if (str->array[i] == delimiter)
{
return i;
}
}
cout << "No occurences of delimiter were found" << endl;
return -1;
}
String* substr(String* str, int start, int end)
{
String* new_str = new String;
int count = 0;
for (int i = start + 1; i < end - 1; i++)
{
new_str->array[count] = str->array[i];
count++;
}
return new_str;
}
void compare(String* str1, String* str2)
{
if (str1->array < str2->array)
{
cout << str1->array << " is less than " << str2->array << endl;
}
if (str1 > str2)
{
cout << str2->array <<" is less than " << str1->array << endl;
}
if (str1 == str2)
{
cout << "The strings are equal" << endl;
}
}
You never allocate memory for the effective CD. You just allocate an array of pointers to CDs (cd_array). This means you have an array of pointers, pointing to unkown memory locations.
The best way to ensure no such bad access is possible, is to not dynamically allocate memory. Just use CD cd_array[max_cds] and work with that. (Use call by reference, if you need to pass this to a function.)
You need to add the following lines:
//Get the artist from .txt file
cd_array[i] = new CD;
cd = cd_array[i];
.....
You should de-allocate the memory once finished using them
for(...)
delete cd_array[i];
delete []cd_array;

Deleting a record from array of pointers of my own class type

I have created an Employee class:
class Employee {
private:
int idNumber;
string name, department, position;
public:
Employee() {
idNumber = 0;
name = department = position = "";
}
Employee(string n, int idn) {
name = n;
idNumber = idn;
department = position = "";
}
Employee(string n, int idn, string dep, string pos) {
name = n;
idNumber = idn;
department = dep;
position = pos;
}
void setName(string n) {
name = n;
}
void setidNumber(int idn) {
idNumber = idn;
}
void setDepartment(string dep) {
department = dep;
}
void setPosition(string pos) {
position = pos;
}
string getName() {
return name;
}
int getidNumber() {
return idNumber;
}
string getDepartment() {
return department;
}
string getPosition() {
return position;
}
};
Now, i created a 2D array of Pointers of type Employee:
int n=2;
Employee **p = new Employee * [n];
for (int i=0; i < n; i++)
p[i] = new Employee;
I stored two records successfully as under:
Name ID Number Department Position
FS 30 CS BS
AT 27 CS BS
I have this code to delete the record of Employees:
string del_name;
int flag = 0;
cin.ignore();
cout << "Enter name: ";
getline(cin, del_name);
for (int i=0; i < n; i++) {
while (del_name == p[i]->getName() && i < n) {
if (del_name == p[i]->getName()) {
delete p[i];
p[i] = NULL;
--k;
++flag;
cout << "Record deleted." << endl;
break;
}
else
{
flag = 0;
}
}
}
if (flag == 0)
cout << "No record found having name " << del_name << "." << endl;
Now, What's the problem:
If a record is found at multiple times. It deletes successfully even if all the records gets deleted.
But if ALL the records are unique and I delete the records one by one and all the records get deleted in this way then the program gets terminated.
Also, is there any other optimized approach to delete records without using VECTORS.
I hope i have clearly explained my problem. I can provide further details if needed.
Thank you for your time
First, usage of std::vector<> or some other container object is the way to go about this. If you can write code that beats (in terms of speed) written by professional library writers, then go ahead.
Second, what is your goal? If it's to simply deallocate entries in that array depending on some criteria, the loop you wrote is overly complex.
bool recordDeleted = false;
for (int i=0; i < n; ++i)
{
if (del_name == p[i]->getName())
{
delete p[i];
p[i] = NULL;
recordDeleted = true;
}
}
if ( !recordDeleted )
{
// record not found
}