How to implement istream& overloading in C++? - c++

I can't get this code to output the file info. How would I use ostream overloading to output this? Or do I have to do it some other way? I can't figure it out.
Which C++ sorting algorithm would be the best to use to sort the info ascending order?
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <stdlib.h> // needed to use atoi function to convert strings to integer
using namespace std;
struct Employees
{
string employeeName;
string employeeID;
int rate;
int hours;
};
istream& operator >> (istream& is, Employees& payroll)
{
char payrollStuff[256];
int pay = atoi(payrollStuff); // use atoi function to convert string to int
is.getline(payrollStuff, sizeof(payrollStuff));
payroll.employeeName = payrollStuff;
is.getline(payrollStuff, sizeof(payrollStuff));
payroll.employeeID = payrollStuff;
is.getline(payrollStuff, sizeof(payrollStuff));
payroll.rate = atoi(payrollStuff);
is.getline(payrollStuff, sizeof(payrollStuff));
payroll.hours = atoi(payrollStuff);
return is;
};
int main()
{
const int SIZE = 5; // declare a constant
Employees payroll_size[5];
ifstream myFile;
myFile.open("Project3.dat");
if(myFile.fail()) //is it ok?
{
cerr << "Input file did not open please check it" << endl;
}
else
for (int i=0; i< 5; i++)
{
myFile >> payroll_size[i];
}
myFile.close();
return 0;
}

Just overload the operator in the same way you did for the >>. For example:
ostream& operator<<(ostream& os, Employees& payroll)
{
os << payroll.employeeName << " " << payroll.employeeID << " " << payroll.rate << " " << payroll.hours << "\n";
return os;
}
Then, in a loop, you can just iterate over the array and print out each of the Employees using <<.
On a side note, if you are checking if the file opened, it is better to use the dedicated function std::ifstream::is_open.
For sorting your entries, it is best that you use std::sort, with a custom predicate for whatever criteria you want to sort with. As an example, if you wanted to sort based on the name of the Employee in alphabetical order, you would use the following command:
sort(payroll_size, payroll_size + 5, [](const Employee& a, const Employee& b) { return a.employeeName < b. employeeName; });

Related

reading a txt file separated by commas into arrays C++ cpp

C++ question!
I have a .txt file with this info:
james, watson
brittany,blake
roger,tra4#pos
jonathan, pote5
amber,Trisa123!
where the first column is name and the second one is the Id of website users.
I need to read this file and then store the information into 2 arrays:
name[]
user_Id []
Could you please help me? I found the solution for saving it into a 2d vector but I prefer to save it as arrays since I need to compare the string values with another string (received by user to check if her name/user Id is already in the system or not)
I found the solution for saving it into a 2d vector but not for arrays.
I will show you your requested solution, but I am sorry to inform you that the solution approach is wrong. For various reasons. First, and most important: In C++ C-Style arrays should in general not not be used.
C-Style arrays have fixed size and are not dyanmic. So, you will always come up with a magic number of an estimated max size. The correct approach would be to use a dynamic container. And for your solution, the std::vector is most appropriate.
Then, it is a very bad idea to have to separate arrays for related data. The correct approach is to put related data in a struct and then create a std::vector of this struct. Otherwise you will have always to maintain and handle always 2 arrays, and you may even lose the sync between related data.
Anyway, I will first show you a solution following your idea:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
const unsigned int MagicNumberForMaxArraySize = 42;
int main() {
// Define Arrays to hold the user and their IDs
string user[MagicNumberForMaxArraySize]{};
string user_ID[MagicNumberForMaxArraySize]{};
// Open the file and check, if it could be opened
ifstream ifs("test.txt");
if (ifs.is_open()) {
unsigned int index = 0;
// Read all lines and put result into arrays
while ((index < MagicNumberForMaxArraySize) and
(getline(getline(ifs, user[index], ',') >> ws, user_ID[index]))) {
// Now we have read a comlete line. Goto next index
++index;
}
// Show debug output
for (unsigned int i = 0; i < index; ++i)
cout << "User: " << user[i] << "\tID: " << user_ID[i] << '\n';
}
else
cout << "\n\n*** Error: Could not open source file\n\n";
}
But I would not recommend to go on with that. The next improvement would be to use a struct and then an array of struct. Additionaly, I will get rid of using namespace std; which should never be used. And, I initialize varaibles with the universal initializer.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
const unsigned int MagicNumberForMaxArraySize = 42;
struct Data {
std::string user{};
std::string ID{};
};
int main() {
// Define array for our needed data
Data data[MagicNumberForMaxArraySize];
// Open the file and check, if it could be opened
std::ifstream ifs("test.txt");
if (ifs.is_open()) {
unsigned int index = 0;
// Read all lines and put result into arrays
while ((index < MagicNumberForMaxArraySize) and
(std::getline(std::getline(ifs, data[index].user, ',') >> std::ws, data[index].ID))) {
// Now we have read a comlete line. Goto next index
++index;
}
// Show debug output
for (unsigned int i = 0; i < index; ++i)
std::cout << "User: " << data[i].user << "\tID: " << data[i].ID<< '\n';
}
else
std::cout << "\n\n*** Error: Could not open source file\n\n";
}
Evolution:
We will now introduce an object oriented principle . Data and methods operating on this data shall be in one class or struct. Hence, we will add IO methods to the struct, and add an aditional struct for holding all users. Also, the new if-statement with initializer can be used. And of course the std::vector.
Please see:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
// Struct to hold properties for one user
struct User {
std::string name{};
std::string ID{};
// Simple extraction
friend std::istream& operator >> (std::istream& is, User& user) {
std::getline(std::getline(is, user.name, ',') >> std::ws, user.ID);
return is;
}
// Simple inserter
friend std::ostream& operator << (std::ostream& os, const User& user) {
return os << "User: " << user.name << "\tID: " << user.ID;
}
};
// This class will contain all users
struct Data {
std::vector<User> users{};
// Simple extraction
friend std::istream& operator >> (std::istream& is, Data& d) {
// Delete potential existing old data
d.users.clear();
// Now read all users
for (User temp{}; is >> temp; d.users.push_back(std::move(temp)));
return is;
}
// Simple inserter
friend std::ostream& operator << (std::ostream& os, const Data& d) {
for (const User& u : d.users) os << u << '\n';
return os;
}
};
int main() {
// Open the file and check, if it could be opened
if (std::ifstream ifs("test.txt");ifs) {
// Read all data and show result
if (Data data{}; not (ifs >> data).bad())
std::cout << data;
}
else
std::cout << "\n\n*** Error: Could not open source file\n\n";
}
You can also use strtok() from cstring library to split string into tokens: Split string in C/C++

Having trouble writing a structure to a binary file C++

I'm trying to make a code that takes a structure, asks for user info and puts the data into a binary file called "output" so it can be read. I tried doing it with my code but it wasn't working. Can anybody help me fix it and tell me what I'm doing wrong?
Here is my code that i am working on.
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string>
#include <iomanip>
#include <stdio.h>
#include <string.h>
using namespace std;
const int NAME_SIZE = 20;
struct Student{
char fname[NAME_SIZE];
int id;
};
int main() {
int choice;
fstream file;
Student person;
cout << "Populate the records of the file." << endl;
file.open("output", ios::out | ios::binary);
cout << "Populating the record with information."<< endl;
cout << "Enter the following data about a person " << endl;
cout << "First Name: "<< endl;
cin.getline(person.fname, NAME_SIZE);
cout << "ID Number: "<< endl;
cin >> person.id;
file.write(reinterpret_cast<char *>(&person), sizeof(person));
file.close();
return 0;
}
I would really appreciate any help
You are mixing between raw binary data and string data . You are writing filename which may have non printable characters after the name if the name is shorter than NAME_SIZE . Also int id is written as an integer which isn't printable .
This storage is valid if you want to store and load binary data like binary protocols .
If you want to store data as readable text you will have to serialize the data first but you won't be able to load them with simple read
the right way is to define a operator<< and operator>> for Student. Then it is a piece of cake dealing with saving and reading the structure.
std::ostream & operator<<(std::ostream & os, Student const & rhs)
{
for(int i=0; i<NAME_SIZE; ++i)
{
os << rhs.fname[i];
}
os << id;
return os;
}
std::istream & operator>>(std::istream & is, Student & rhs)
{
for(int i=0; i<NAME_SIZE; ++i)
{
is >> rhs.fname[i];
}
is >> id;
return is;
}
So when you need to save to file you just do:
file << person;
when you need to read from it:
file >> person;
P.S.: I would suggest to make the operator implementations more robust than that, maybe through special markers, so that you can detect issues while reading from the file.

Reading .txt file into array of struct

I'm a beginner in programming and i'm trying to read my .txt file into an array of struct in this program which after that display the data and then sort it, but the program only reads the first line and the loop won't stop until arraysize.
The file data looks like this:
ID NAME ADDRESS AGE
The Code:
#include <iostream>
#include <fstream>
#include <string>
#include <conio.h>
using namespace std;
struct bio
{
char name[50], address[50];
int id, age;
};
int main()
{
int i = 0, arraysize = 1000;
bio b[arraysize];
fstream data;
data.open("biodata.txt");
while(data.read((char*)&b, sizeof(b[i])))
{
for (i = 1; i < 1000; i++)
{
data >> b[i].id >> b[i].name >> b[i].address >> b[i].age;
}
}
for (i = 0; i < 1000; i++)
{
cout << b[i].id << " " << b[i].name << " " << b[i].address << " " << b[i].age << " " << endl;
}
data.close();
getch();
}
#include <iostream>
#include <fstream>
#include <string>
#define ARRAY_SIZE 1000
#define FILE_NAME "biodata.txt"
using namespace std;
struct Bio
{
int m_id;
string m_name;
string m_address;
int m_age;
};
int main()
{
Bio bio[ARRAY_SIZE];
ifstream data;
data.open(FILE_NAME);
if (!data)
{
cout << "not file " << FILE_NAME;
return 0;
}
for (int i = 0; i < ARRAY_SIZE && data.good(); ++i)
{
data >> bio[i].m_id >> bio[i].m_name >> bio[i].m_address >> bio[i].m_age;
}
for (int i = 0; i < ARRAY_SIZE ; ++i)
{
cout << bio[i].m_id << " " << bio[i].m_name << " " << bio[i].m_address << " " << bio[i].m_age << " " << endl;
}
data.close();
}
a few comments:
for what conio lib?
struct (bio) start with capital letter
don't use in char array in c++, you have string for this.
separate the variables to separate lines (bot "char name[50], address[50];")
better to rename members to m_X
about your "arraysize". if it const number you decide, do it with #define. if you need the whole file, you don't need it at all. (the file name too)
ifstream and not fstream data. you need just read. you don't want to change your data with some mistake.
check it the file opened well
in your code you check the while just before the loop.
in your condition loop check data.good(). it check it not eof and he file is readable.
read command is for binary file
it's better to separate the load file and print data to 2 differents functions. I didn't do it for save on your template
The following is maybe a little complicated for beginners, but since we are talking about C++, we should look also to a "more" objective oriented approach.
You designed a class, called bio. In object oriented languages you will put all data for an object and also all functions that operate on this data in the class. So you need to add member functions. The idea is that you encapsulate all data in an object. The outside world should not know anything about the details of the class. You just access it via member functions. And if you want to make changes later than you will do this within the member functions of the classes. And the rest of the program will continue to work.
Additionally we should definitely use C++ language features. For examples you should use std::string for strings and not Plain old C-Style char arrays. You should basically never use C-Style arrays in C++. Instead, please use STL container.
So, then let's design a class with data members and member functions. Since at the moment we just need input and output functionality, we overwrite the inserter and extractor operator. These operators know abot the data and behaviour of the class and will take care.
See the following program:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <sstream>
struct Bio
{
// Data
unsigned int id{};
std::string name{};
std::string address{};
unsigned int age{};
// Overload extractor operator to read all data
friend std::istream& operator >> (std::istream& is, Bio& b) {
std::string textLine{};
if (std::getline(is, textLine)) {
std::istringstream textLineStream{textLine};
textLineStream >> b.id >> b.name >> b.address >> b.age;
}
return is;
}
// Overload inserter operator to print the data
friend std::ostream& operator << (std::ostream& os, const Bio& b) {
return os << b.id << " " << b.name << " " << b.address << " " << b.age;
}
};
std::istringstream sourceFile{R"(1 John Address1 31
2 Paul Address2 32
3 Ringo Address3 33
4 George Address4 34
)"};
int main()
{
// Define Variable and read complete source file
std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
// Sort the guys by name
std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
// Show output on screen
std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
return 0;
}
Some comments. On StackOverflow, I cannot use files. So in my example program, I use a std::istringstream instead. But this is also an std::istream. You can use any other std::istream as well. So if you define an `````std::ifstreamto read from a file, then it will work in the same way as thestd::istringstream````.
And please see. The extractor operator does the whole work of reading the source File. It is encapsulated. No outside function needs to know, how it does.
In the main function, we define a std::vector and use its range contructor to specifiy where the data comes from. We give it the std::istream_iterator, which iterates over the input data and calls the extractor operator until verything is read.
Then we sort by names and copy the result to the output.
You may notice that fields in your input data are separted by space. This does in general not work for none atomic data. The name could exist of 2 parts and the address can have a street and a city. For this CSV (Comma separated Values) files have been invented.
Please see a more realistic soultion below.
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <sstream>
#include <regex>
struct Bio
{
// Data
unsigned int id{};
std::string name{};
std::string address{};
unsigned int age{};
// Overload extractor operator to read all data
friend std::istream& operator >> (std::istream& is, Bio& b) {
std::string line{};
std::regex re{";"};
if (std::getline(is, line)) {
std::vector<std::string> token{std::sregex_token_iterator(line.begin(), line.end(), re, -1), std::sregex_token_iterator()};
if (4 == token.size()) {
b.id = std::stoul(token[0]);
b.name = token[1];
b.address = token[2];
b.age = std::stoul(token[3]);
}
}
return is;
}
// Overload inserter operator to print the data
friend std::ostream& operator << (std::ostream& os, const Bio& b) {
return os << b.id << ", " << b.name << ", " << b.address << ", " << b.age;
}
};
std::istringstream sourceFile{R"(1; John Lenon; Street1 City1; 31
2; Paul McCartney; Street2 City2; 32
3; Ringo Starr; Street3 City3; 33
4; George Harrison; Address4; 34
)"};
int main()
{
// Define Variable and read complete source file
std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
// Sort the guys by name
std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
// Show output on screen
std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
return 0;
}
We have a new source format and main is unchanged. Just the extractor operator is modified. Here we are using a different iterator to get the source data.

Why do I see a seg fault in this?

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;
class student
{
public:
string s;
int age;
};
ostream& operator<<( ostream &output,const student &s)
{
output << s.s << " " << s.age << "\n";
return output;
}
istream& operator >>(istream& in, student& val)
{
return in >> val.s >> val.age;
}
bool valuecmp(const student & a, const student & b)
{
return a.s < b.s;
}
int main (void)
{
vector<student> a;
student b;
cin>>b.s;
cin>>b.age;
fstream myfile;
myfile.open("a1.txt",ios::app);
int i = 0;
myfile << b;
cout<<"File has been written"<<"\n";
myfile.open("a1.txt",ios::in);
for (string line; getline(myfile, line);)
{
istringstream stream(line);
student person;
stream >> person;
a.push_back(person);
cout<<a[i].s<<a[i].age<<"\n";
i++;
}
sort(a.begin(),a.end(),valuecmp);
fstream my;
my.open("a2.txt",ios::out);
student c;
for ( i = 0; i < 2; i++ )
{
cout<<a[i].s<<a[i].age<<"\n";
c = a[i];
my << c;
}
return 0;
}
A simple program to accept user input, store in an object. I have multiple inputs already in the file before running this program. So, when I made this program to accept the input from the user, write in a file, and then sort the contents of the entire file, using, the sort operation and then write the sorted output in a new file.
This accepts user input, shows the message, File has been written, but then shows a seg fault. Why is it happening?
You're getting a segfault on this line
cout<<a[i].s<<a[i].age<<"\n";
because a is empty when you try to access it. a.push_back(person) never gets called so no data is added to your vector. getline() fails the first time.
Hope this leads you in a helpful direction.

Display their names in ascending order with respect to their ages?

The stated problem is "Write a program that takes names and ages of 10 employees as input in a 2D char array and display their names in ascending order with respect to their ages".
I've pasted below what I have so far, but I don't know how to display the names in ascending order with respect to age. What am I missing?
#include<iostream.h>
#include<string.h>
void sort(int[],int);
void main(void)
{
char a[10][5],x;
int b[5],i,j;
for(j=0;j<=5;j++)
{
cout<<"Enter name of Employee:";
for(i=0;i<10;i++)
{
cin>>x;
x=a[j][i];
}
cout<<"Enter Employee's age:";
cin>>b[j];
cout<<endl;
}
sort(b,j);
cout<<"Name of Employee \t\t Age \n";
for(j=0;j<5;j++)
{
for(i=0;i<10;i++)
{
x=a[j][i];
cout<<"\t"<<x;
}
cout<<"\t\t"<<b[j]<<"\n";
}
}
void sort(int x[],int size)
{
int temp,i,j;
for(j=0;j<size;j++)
{
for(i=0;i<size;i++)
{
if(x[i]>x[i+1])
{
temp=x[i+1];
x[i+1]=x[i];
x[i]=temp;
}
}
}
}
I will point you in the right direction.
You should define struct to hold a Name and age pair, then make a comparison function.
Then you simply need to populate a vector of your structs then sort them using the sorting function provided by the standard library, iterate through the vector printing out the contents, you can define stream operator of the struct you made to simplify them.
http://www.cplusplus.com/doc/tutorial/structures/
http://en.cppreference.com/w/cpp/algorithm/sort
http://en.cppreference.com/w/cpp/container/vector
http://www.java2s.com/Code/Cpp/Overload/Overloadstreamoperator.htm
EDIT: and please for all that is good, use a std::string to hold the names.
http://www.cplusplus.com/reference/string/string/
using an char array like you are is pretty much deprecated.
I started writing this before the other solution, because I would rather see what C++ is capable of not what your lecture is feeding you.
#include <string>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <fstream>
struct employee
{
employee(const std::string& name_, unsigned age_)
: name(name_), age(age_) {}
std::string name;
unsigned age;
};
std::ostream& operator<<(std::ostream& os, const employee& e)
{
os << e.name << " " << e.age;
return os;
}
bool comp_age(const employee& e1, const employee& e2)
{
return e1.age<e2.age;
}
int main()
{
std::vector<employee> employees;
employees.reserve(5);
for(unsigned i=0; i!=5; ++i)
{
std::string name;
unsigned age;
std::cout << "Name:" << std::flush;
std::cin >> name;
std::cout << "Age:" << std::flush;
if(!std::cin >> age)
{
std::cerr << "not an number" << std::endl;
--i;
continue;
}
//note you should catch any failure to parse to int here
employees.push_back(employee(name, age));
}
std::sort(employees.begin(), employees.end(), comp_age);
std::copy( employees.begin(), employees.end(),
std::ostream_iterator<employee>(std::cout, "\n"));
return 0;
}
So this is an alternative, but PLEASE learn the concepts I list above which constitutes this example.
You should start by changing void main to int main, because void main is not valid C++. Then you change the #includes to be real C++ header files not what you have:
#include <iostream>
#include <string>
Then you should give meaningful names to your variables. It's impossible to follow the code in a sea of as and bs.
Then you need to sort the employees by their age. You already have a sorting algorithm written there, so you only need to adapt it to swap all the employee data instead of swapping just their ages, while still doing the comparisons only on their age. This will be easier if instead of two separate arrays for the employee data, you kept instead a single array of a structure that holds both the name and age of the employee:
struct employee {
std::string name;
int age;
};
Once sorted, you can just loop through all the employees an print their names.
I believe there are a few more issues in the code, but they should be easy to fix with a good reference and some debugging time. If you have trouble, you can post a new question with what you've got so far, and explain what is stumping you.
This would all be much better if you just used the C++ standard library, but unfortunately some teachers decide to teach Crippled++ to their students instead of proper C++ :(
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// treat an employee as a single unit
struct employee {
std::string name;
int age;
};
// a comparison function to compare two employees by their age
// should return true if the first one is younger than the second one
// this will be used for sorting later
bool is_younger(employee const& l, employee const& r) {
return l.age < r.age;
}
int main()
{
// a vector with 5 employees
std::vector<employee> employees(5);
for(j=0;j<=5;++j)
{
std::cout << "Enter name of Employee: "
// read the entire name at once
getline(std::cin, employees[j].name);
std::cout << "Enter Employee's age:";
// read the age
std::cin >> employees[j].age;
}
// sort all the employees using the comparison function written above
std::sort(employees.begin(), employees.end(), is_younger);
std::cout<<"Name of Employee \t\t Age \n";
for(j=0;j<5;++j)
{
std::cout << employees[j].name;
std::cout << "\t\t" << employees[j].age << "\n";
}
}
Use a std::multimap (a key/value store, fitting your needs), which is weakly sorted ascending by default. Your key is the age and the value is the name. For printing the values just use iterators. std::multimap is chosen because there could potentially be employees with the same age.
#include <iostream.h>
int main() {
int a;
int b;
int sum;
std::cout << "Enter two numbers" << std::endl;
std::cout << "Enter the value of a" << std::endl;
std::cin >> a;
std:: cout << "Enter the value of b" << std::endl;
std::cin >> b;
sum = a + b;
std::cout << sum << std::endl;
return 0;
}