So I'm practicing working with files in C++ a bit. I've created a simple program that loads Student's name, index number, date of birth and grades from STUDENT.txt, counts the student's average, and writes it all down in a form below to a different file.
It all works like a charm, except one thing: It eats up the first letter of every line. EXAMPLE:
I have a STUDENT.txt file that looks like this:
Alice Cooper
225883
21/6/1986
6,6,8,9,10
Zakk Wylde
27568
14/5/1978
6,6,6,6,6
So the first student, Cooper, will be processed correctly, but everyone else in the file won't. They'll be written down as 'akk Wylde'...
So everything but a name is OK.
I was hoping someone could tell me what exactly is going on, I'm guessing it's eating up a '\n' and another character, but I couldn't find it with debugging.
// Student_datoteka.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
std::string toString(int day, int month, int year) {
std::string d, m, g, ret;
d = std::to_string(day);
m = std::to_string(month);
g = std::to_string(year);
ret += d;
ret += "/";
ret += m;
ret += "/";
ret += g;
return ret;
}
int main()
{
std::fstream load("STUDENTI.txt", std::ios::in);
std::ofstream write("IZVJESTAJ.txt");
write << std::setw(30) << std::left << "Student"
<< std::setw(10) << "Indeks"
<< std::setw(20) << "Datum rodjenja"
<< std::setw(10) << "Prosjek" << std::endl;
write << std::setw(30) << std::left << "-------"
<< std::setw(10) << "------"
<< std::setw(20) << "--------------"
<< std::setw(10) << "-------" << std::endl;
if (!write)
std::cout << "ERROR!";
std::string name;
int indeks(0), day(0), month(0), year(0);
char sign(0), sign2(0);
int grades[30];
double average(0);
while (std::getline(load, name)) {
if (load.eof()) break;
load >> indeks;
load >> day >> sign >> mjesec >> sign2 >> year;
int i(0);
while (load >> grades[i]) {
load >> sign;
average += grades[i];
i++;
}
average /= double(i);
std::string datum = toString(day, month, year);
write << std::setw(30) << std::left << name
<< std::setw(10) << indeks
<< std::setw(20) << datum
<< std::setw(10) << std::setprecision(2)
<< average<< std::endl;
average = 0;
if (load.eof()) break;
load.clear();
}
return 0;
}
This is the output: http://prntscr.com/jvm3jn
I have made a small improvements on your code so please, if you don't like, take only the working part. Note that my code can be improved and generalized even more but I am leaving that to you.
Hope you would like my solution:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
typedef struct Student
{
std::string name;
int index_no;
int day_of_birth;
int month_of_birth;
int year_of_birth;
std::vector<int> grades;
double avg_grade;
} Student;
std::string date_to_string(int day, int month, int year)
{
std::string d, m, g, ret;
d = std::to_string(day);
m = std::to_string(month);
g = std::to_string(year);
ret += d;
ret += "/";
ret += m;
ret += "/";
ret += g;
return ret;
}
void get_date_from_str(int& day, int& month, int& year, std::string const& str)
{
std::stringstream ss(str);
ss >> day;
ss.ignore(1);
ss >> month;
ss.ignore(1);
ss >> year;
}
void get_numbers_from_str(std::vector<int>& vec, std::string const& str)
{
std::stringstream ss(str);
int i;
while (ss >> i)
{
vec.push_back(i);
ss.ignore(1);
}
}
double calculate_avg(std::vector<int> const& vec)
{
int sum = 0;
for (auto item : vec)
{
sum += item;
}
return (double) sum / vec.size();
}
int main()
{
std::ifstream students_file("students.txt");
std::ofstream report_file("report.txt");
if (students_file.is_open() && report_file.is_open())
{
report_file << std::setw(30) << std::left << "Student"
<< std::setw(10) << "Indeks"
<< std::setw(20) << "Datum rodjenja"
<< std::setw(10) << "Prosjek" << std::endl;
report_file << std::setw(30) << std::left << "-------"
<< std::setw(10) << "------"
<< std::setw(20) << "--------------"
<< std::setw(10) << "-------" << std::endl;
std::vector<Student> students;
std::string name;
while (std::getline(students_file, name))
{
std::string index_no;
std::string date_of_birth;
std::string grades;
Student student;
student.name = name;
std::getline(students_file, index_no);
std::stringstream(index_no) >> student.index_no;
std::getline(students_file, date_of_birth);
std::getline(students_file, grades);
get_date_from_str(student.day_of_birth, student.month_of_birth, student.year_of_birth, date_of_birth);
get_numbers_from_str(student.grades, grades);
student.avg_grade = calculate_avg(student.grades);
students.push_back(student);
}
for (auto student : students)
{
report_file << std::setw(30) << std::left << student.name
<< std::setw(10) << student.index_no
<< std::setw(20) << date_to_string(student.day_of_birth, student.month_of_birth, student.year_of_birth)
<< std::setw(10) << std::setprecision(2)
<< student.avg_grade << std::endl;
}
}
else
{
std::cout << "Unable to open one of the files!" << std::endl;
}
return 0;
}
First, I think you have complicated your code for both, reading and understanding it, while you have some C++ features that can simplify this part with signs and parsing date and grades.
I have also created struct Student and I have split some of the code into functions.
And, regarding your solution. As someone already mentioned in the comment, fault is in these lines:
while (load >> grades[i])
{
load >> sign;
average += grades[i];
i++;
}
You are 'eating' one character more than you need. And this character is the first character of the name of the second student. Therefore, if you change your code to following, you'll see what I'm talking about:
while (load >> grades[i])
{
if (i != 4) // just a temporary solution, since the first student has 5 grades
load >> sign;
average += grades[i];
i++;
}
Related
I declared a vector<string> and I cannot even compile it. I tried many ways but none of them worked.
I'm trying to write out the x.surname.push_back(word)[i] but it's definetly written wrongly and I have no idea how to write it properly and make it possible to compile.
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int number, i = 0;
string word;
struct donators {
vector<string> surname;
vector<int> amount;
} x;
cout << "How many donators do you want to register? " << endl;
cin >> number;
for (i = 0; i < number; i++) {
cout << "Surname: ";
cin >> word;
x.surname.push_back(word)[i];
cout << "Amount: ";
x.amount.push_back(i);
cin >> x.amount[i];
}
cout << "OUR GORGEUS DONATORS: " << endl;
for (i = 0; i < number; i++) {
if (x.amount[i] >= 10000) {
cout << "Surname: " << x.surname(word)[i];
cout << "Amount: " << x.amount[i] << endl;
}
else if (x.amount[i] < 10000) {
cout << "Lack of surnames!" << endl;
}
}
cout << "OUR CASUAL DONATORS: " << endl;
for (i = 0; i < number; i++) {
if (x.amount[i] < 10000) {
cout << "Surname: " << x.surname(word)[i];
cout << "Amount: " << x.amount[i] << endl;
} else if (x.amount[i] >= 10000) {
cout << "Lack of surnames!" << endl;
}
}
return 0;
}
And one more thing. How to make sentence "Lack of surnames!" to be written out once? In some cases, it is written out twice or more times what is redundant.
You are putting [i] at seemingly random places in your code. Such as in x.surname.push_back(word)[i];. Don't add things like this to your code if you're unsure about what they're doing.
The x.surname(word)[i] construct are also wrong. What's x.surname(word) supposed to be? This syntax is for function calls. surname, however, is not a function. It's a std::vector<std::string>. Just put x.surname[i] instead.
And one more thing. How to make sentence "Lack of surnames!" to be
written out once? In some cases, it is written out twice or more times
what is redundant.
That's because you write it for every donor that doesn't fit the criterion. Instead, keep track if any donor fits the criterion and only print it when none ends up fitting. You can do it like this:
bool HasGorgeousDonators = false;
And then in the loop:
if (x.amount[i] >= 10000)
{
cout << "Surname: " << x.surname[i];
cout << "Amount: " << x.amount[i] << endl;
HasGorgeousDonators = true;
}
And after the loop:
if (!HasGorgeousDonators)
cout << "Lack of surnames!" << endl;
Likewise for the other loop. Also, please consider the following Q&A:
Why is "using namespace std;" considered bad practice?
It seems like you are writing C with some C++ help functions. However C++ is a different language. Sure, it supports some C structures, but there's so much more.
Take a look at some of my suggestions for implementation and compare it to your code:
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
template<typename T>
T ReadCin(std::string_view const& sv = "") {
T retVal;
if (!sv.empty()) std::cout << sv;
std::cin >> retVal;
return retVal;
}
class Donator {
private:
std::string surname{};
int amount{};
public:
constexpr bool IsGenerous() const noexcept { return amount >= 10000; }
void Read() noexcept {
surname = ReadCin<decltype(surname)>("Surname: ");
amount = ReadCin<decltype(amount)>("Amount: ");
}
friend std::ostream& operator<<(std::ostream& out, Donator const& donator) noexcept {
out << "Surname: " << donator.surname << ", " << "Amount: " << donator.amount;
return out;
}
};
int main() {
std::vector<Donator> donators(ReadCin<int>("How many donators do you want to register?\n"));
for (auto& donator : donators) donator.Read();
std::cout << "OUR GENEROUS DONATORS:\n";
std::copy_if(std::cbegin(donators), std::cend(donators), std::ostream_iterator<Donator>(std::cout, "\n"),
[](Donator const& donator) { return donator.IsGenerous(); });
std::cout << "OUR CASUAL DONATORS:\n";
for (auto const& donator : donators) if (!donator.IsGenerous()) std::cout << donator << '\n'; //alternative
}
I tried to include some of the possibilities using C++. I would really advise you to get a good book on C++.
I am the new kid in the block, studying C++. I have loaded a file stream in a list container, using variables. I want to be able to access and change the value of any of those variables. I've been trying for weeks to no avail. Can somebody help?
This is the external text file: flightBoard1.txt
Delta 3431 Paris JFK
Usair 2275 EWR London
Delta 1500 Bonn Milan
This is the main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <list>
using namespace std;
template<class T, class U, class V>
void changeFlight(list<string> &myFlight, T loc, U value, V newVal);
int main()
{
string _company;
int _flight;
string _origin;
string _destination;
list<string> flightBoard;
stringstream *ssPtr;
int counter = 0;
ifstream myFile("flightBoard1.txt");
while(myFile >> _company >> _flight >> _origin >> _destination ){
++counter;
ssPtr = new stringstream; // you want to put each line in a different slot in the flightBoard list object
*ssPtr << counter << " " << _company << "\t" << _flight << "\t" << _origin << "\t" << _destination << endl;
flightBoard.push_back(ssPtr->str()); // You need an arrow, this is a pointer
}
list<string>::iterator it;
for(it = flightBoard.begin(); it != flightBoard.end(); it++){
cout << *it ;
}
int oldFlight, newFlight;
cout << endl << "Enter old flight number: ";
cin >> oldFlight;
cout << "Enter new flight number: ";
cin >> newFlight;
changeFlight(flightBoard, ssPtr, oldFlight, newFlight);
delete ssPtr;
myFile.close();
return 0;
}
template<class T, class U, class V>
void changeFlight(list<string> &myFlight, T loc, U value, V newVal){
list<string>::iterator it;
cout << endl << "Flight: " << value << " has been changed to: " << newVal << endl;
for(it = myFlight.begin(); it != myFlight.end(); it++){
// THIS IS WHERE I AM HAVING A PROBLEM
// PLEASE UN-COMMENT BELOW TO SEE PROBLEM
/*if(it -> myFlight -> loc -> value){
value = newVal;
}*/
}
}
To solve your problem I think you should use the better structure for storage of your flights. string is not a good type if you need to manipulate data further. I suggest to introduce class Flight:
class Flight
{
public:
string company;
int flight;
string origin;
string destination;
Flight(const string& _company, int _flight, const string& _origin, const string& _destination)
: company(_company), flight(_flight), origin(_origin), destination(_destination)
{
}
string ToString() const
{
stringstream printer;
printer << company << "\t" << flight << "\t" << origin << "\t" << destination << endl;
return printer.str();
}
};
Also there are a number of problems in code snippet you have posted.
Memory leak inside while loop. You are allocating ssPtr = new stringstream; on each iteration, but delete it only once at the end.
changeFlight has too much template type arguments. If U value should be changed to V newVal inside changeFlight probably it should have the same types.
Hard to change type of flightBoard, list<string> copied everywhere. It's better to create typedef for list<string> type to make your code simpler. E.g. typedef list<string> FlightListType;.
Here is your code with all the mentioned problems fixed:
typedef list<Flight> FlightListType;
template<class T>
void changeFlight(FlightListType& myFlight, T value, T newVal);
int main()
{
string _company;
int _flight;
string _origin;
string _destination;
FlightListType flightBoard;
ifstream myFile("flightBoard1.txt");
while(myFile >> _company >> _flight >> _origin >> _destination )
{
flightBoard.push_back(Flight(_company, _flight, _origin, _destination));
}
FlightListType::const_iterator it;
int counter = 0;
for(it = flightBoard.begin(); it != flightBoard.end(); it++)
{
cout << counter << " " << (*it).ToString();
++counter;
}
int oldFlight, newFlight;
cout << endl << "Enter old flight number: ";
cin >> oldFlight;
cout << "Enter new flight number: ";
cin >> newFlight;
changeFlight(flightBoard, oldFlight, newFlight);
myFile.close();
return 0;
}
template<class T>
void changeFlight(FlightListType& myFlight, T value, T newVal)
{
FlightListType::iterator it;
cout << endl << "Flight: " << value << " has been changed to: " << newVal << endl;
for(it = myFlight.begin(); it != myFlight.end(); it++)
{
if ((*it).flight == value)
{
// TODO: Here you can do with the Flight what ever you need
// For example change it's number
(*it).flight = newVal;
}
}
}
I am trying this piece of code in vs 2008
#include <stdio.h>
#include <iostream>
#include <string>
typedef struct _first
{
int age;
std::string name;
}first;
typedef struct _second
{
int age;
char name[20];
}second;
void copy_structure()
{
first s;
second f;
f.age = 15;
cout<<"Enter the name"<<endl;
fgets(f.name, 20, stdin);
memcpy(&s,&f,20);
cout << "Name: " << s.name << endl;
cout << "Age: "<< s.age << endl;
}
int main()
{
copy_structure();
return 0;
}
while building I didn't get any error but when I run, name field is empty over here
cout << "Name: " << s.name << endl;
I am not getting any output over here, can somebody help me to solve this issue.
You should use an approach based on member-wise copying. For example
void copy_structure()
{
first f;
^^
second s;
^^
s.age = 15;
cout<<"Enter the name"<<endl;
fgets(s.name, 20, stdin);
f.age = s.age;
f.name = s.name;
cout << "Name: " << f.name << endl;
cout << "Age: "<< f.age << endl;
}
Otherwise the internals of the object name of the type std::string will be overwritten and the program will have undefined behaviour.
This looks like C but not like C++... Your current code will also brick your std::string instance. memcpy is dangerous and should not be used, unless you have a very, very good reason. I never had a reason for this so far.
My suggestion:
#include <iostream>
#include <string>
using namespace std;
struct second
{
int age;
char name[20];
};
struct first
{
int age;
string name;
first& operator=(const second& rhs);
};
// some operator for copying
first& first::operator=(const second& rhs)
{
age = rhs.age;
name = rhs.name;
return *this;
}
int main()
{
first s;
second f;
f.age = 15;
cout << "Enter your name" << endl;
cin >> f.name;
s = f;
cout << "Name: " << s.name << endl;
cout << "Age: " << s.age << endl;
return 0;
}
This is improvable, of course. You would usually rather use classes than structs. And you would might also have an operator>> for second.
This is an assignment for a course that I am having problems with. Until now I thought I was fairly familiar with vectors in C++. This program is supposed to take a file from the user calculate the users pay then spit back out in a nice table all the information relevant.
It must contain a vector of struct and I must use push_back. I get two errors that I cannot figure out at this moment. In the for loop at the end of main() it tells me a reference type of vector cannot be initialized with Employee. The two functions after ward tell me that I for example .HoursWorked is not a member of the struct.
I tried looking around other questions for help but they all mentioned not using pointers which I'm 100% positive there are no pointers in my program. The txt file I am using for testing the program looks as follows:
John Smith 123-09-8765 9.00 46 F
Kevin Ashes 321-09-8444 9.50 40 F
Kim Cares 131-12-1231 11.25 50 P
Trish Dish 141-51-4564 7.52 24 P
Kyle Wader 432-12-9889 5.75 48 F
Code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>
using namespace std;
struct Employee
{
string name;
string ssn;
double hourlyWage;
double HoursWorked;
char status;
double straightTimePay;
double overTimePay;
double netPay;
};
void calculatePay(vector<Employee>& buisness);
void displayEmployee(vector<Employee> buisness);
int main()
{
vector<Employee> buisness;
string fileName, line;
ifstream inFile;
stringstream ss;
cout << "Please input the file name to read: ";
cin >> fileName;
inFile.open(fileName);
if (!inFile)
{
cout << "Cannot open file " << fileName << " Aborting!" << endl;
exit(1);
}
int index = 0;
string firstName, lastName;
getline(inFile, line);
while (!inFile.eof())
{
if (line.length() > 0)
{
ss.clear();
ss.str(line);
buisness.push_back;
ss >> firstName >> lastName;
buisness[index].name = firstName + " " + lastName;
ss >> buisness[index].ssn;
ss >> buisness[index].hourlyWage >> buisness[index].HoursWorked;
ss >> buisness[index].status;
index++;
}
getline(inFile, line);
}
inFile.close();
cout << "The information of the buisness:" << endl;
cout << setw(20) << "Name" << setw(15) << "SSN" << setw(12) << "Hourly Wage";
cout << setw(14) << "Hours Worked" << setw(18) << "Straight Time Pay";
cout << setw(14) << "Over Time Pay" << setw(6) << "Status" << setw(10) << "Net Pay" << endl;
for (index = 0; index < buisness.size(); index++)
{
calculatePay(buisness[index]);
displayEmployee(buisness[index]);
}
return 0;
}
void calculatePay(vector<Employee>& buisness)
{
if (buisness.HoursWorked <= 40)
{
buisness.straightTimePay = buisness.hourlyWage * buisness.HoursWorked;
buisness.overTimePay = 0;
}
else
{
buisness.straightTimePay = buisness.hourlyWage * 40;
buisness.overTimePay = buisness.hourlyWage * 1.5 * (buisness.HoursWorked - 40);
}
buisness.netPay = buisness.straightTimePay + buisness.overTimePay;
if (buisness.status == 'F')
buisness.netPay -= 10;
}
void displayEmployee(vector<Employee> buisness)
{
int precisionSetting = cout.precision();
long flagSettings = cout.flags();
cout.setf(ios::fixed | ios::showpoint);
cout.precision(2);
cout << setw(20) << buisness.name << setw(15) << buisness.ssn << setw(12) << buisness.hourlyWage;
cout << setw(14) << buisness.HoursWorked << setw(18) << buisness.straightTimePay;
cout << setw(14) << buisness.overTimePay << setw(6) << buisness.status << setw(10) << buisness.netPay << endl;
cout.precision(precisionSetting);
cout.flags(flagSettings);
}
At the very least.. You have the line:
calculatePay(buisness[index]);
So clearly we all calling a function calculatePay and we're passing it an Employee.
But your function prototype says that it takes a std::vector<Employee>. You probably intended for the functions to take Employee & instead.
You should call vector push_back with the element to put in:
Employee employee;
// assign values to employee
ss << employee.ssn;
ss << employee.name;
business.push_back(employee);
Although the compiler error logs seem tedious, but you can almost always get enough information from the error logs. Compiling under gcc 4.2.1, the error logs says :
error: invalid initialization of reference of type ‘std::vector<Employee, std::allocator<Employee> >&’ from expression of type ‘Employee’ on the line of calling method calculatePay(). We can infer that you passed Employee to and function which want an vector of Employee as parameter. You can fix this by change calculatePay(vector<Employee>& buisness) to calculatePay(Employee& buisness). And that will fix error: ‘class std::vector<Employee, std::allocator<Employee> >’ has no member named ‘HoursWorked’
I'm completely new to C++ and currently I'm trying to read very basic text file which look like this:
Dr John Doe
British
2
Soccer
Swimming
and my expected output should look like:
My information
Name: John Doe
Nationality: British
I have 2 hobbies:
1. Soccer
2. Swimming
My header file:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
const int MAX = 80;
const int MAXNO = 5;
enum Title {Miss, Mrs, Mr, Dr, Unknown};
struct Date
{
int day;
int month;
int year;
};
struct MyInfo
{
char name [MAX];
char national [MAX];
int noOfHobbies;
char hobby [MAXNO][MAX];
};
void getMyInfo (fstream& , char[] , MyInfo&);
void displayMyInfo (MyInfo);
My functions:
#include "Lab_1.h"
void getMyInfo (fstream& afile,char fileName[], MyInfo& x) {
afile.open (fileName);
if (!afile)
{
cout << "Binary file " << fileName << " opened for creation failed" << endl;
exit (-1);
}
cout << "\n" << "Begin reading of " << fileName << endl;
string line;
while(getline(afile, line))
{
afile >> x.national;
afile >> x.noOfHobbies;*/
if (afile >> x.name >> x.national >> x.noOfHobbies) {
cout << "Name: " << x.name << ", "
<< "National: " << x.national << ", "
<< "noOfHobbies: " << x.noOfHobbies << ", "
<< endl;
}
}
}
void displayMyInfo (MyInfo x) {
}
My main function:
#include "Lab_1.h"
int main () {
fstream afile;
MyInfo x;
string fileName;
getMyInfo(afile,"textfile.txt",x);
//displayMyInfo(x);
afile.close ();
}
The above code output nothing because I just put everything I understand over the forum with similar question. Since I'm already stuck for 1 day even though I've already done a lot of research but most of them suggest to use vector which I'm not familiar with at this moment, so can someone give me a solution to this problem? Thank you very much for your help in advance.
Random act of madness kindness:
Live On Coliru
#include <fstream>
#include <set>
struct Person {
std::string name;
std::string nationality;
std::set<std::string> hobbies;
friend std::istream& operator>>(std::istream& is, Person& into) {
size_t n = 0;
if (getline(is, into.name) &&
getline(is, into.nationality) &&
is >> n && is.ignore(1024, '\n'))
{
while (n--) {
std::string hobby;
if (getline(is, hobby))
into.hobbies.insert(hobby);
else
is.setstate(std::ios::failbit);
}
}
return is;
}
};
#include <iostream>
int main() {
std::ifstream ifs("input.txt");
Person p;
if (ifs >> p) {
std::cout << "My information\n";
std::cout << p.name << "\n";
std::cout << p.nationality << "\n";
std::cout << "I have " << p.hobbies.size() << " hobbies:\n";
size_t counter = 0;
for(auto const& hobby : p.hobbies) {
std::cout << ++counter << ". " << hobby << "\n";
}
} else {
std::cerr << "Parse failure\n";
}
}