In binary file, there are some information which is student id, name and GPA for three students. I want to read them. id is integer type and GPA is float type. name is string type and the size is not fixed because the size of name is different each other. I want to get id, name and GPA for each student at function of student class.
probably, I think I have a problem at loading name.
I want to know how to fix it.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Student {
private:
int id;
string name;
float GPA;
string line;
public:
Student() {};
Student(string line_) :line(line_) { };
int get_id();
string get_name();
float get_GPA();
void save_bi(ofstream &of);
void load_bi(ifstream &inf);
void save_txt(ofstream &of);
void print();
};
void Student::load_bi(ifstream &inf)
{
inf.read((char*)&id, sizeof(id));
inf.read((char*)&name, sizeof(name));
inf.read((char*)&GPA, sizeof(GPA));
//a.push_back(name);
}
int main()
{
ifstream inf;
inf.open("student_data.bin", ios::in | ios::binary);
if (!inf.is_open())
cout << "file open failure." << endl;
Student A;
A.load_bi(inf);
A.print();
Student B;
B.load_bi(inf);
B.print();
Student C;
C.load_bi(inf);
C.print();
inf.close();
return 0;
}
it is error message.
inline void _Container_base12::_Orphan_all() noexcept
{ // orphan all iterators
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myproxy != nullptr)
{ // proxy allocated, drain it
_Lockit _Lock(_LOCK_DEBUG);
for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
*_Pnext != nullptr; *_Pnext = (*_Pnext)->_Mynextiter)
(*_Pnext)->_Myproxy = nullptr;
_Myproxy->_Myfirstiter = nullptr;
}
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
}
Exception thrown: read access violation.
_Pnext was 0x13AED64.
A simple example of serializing your structure:
class Student
{
int id;
string name;
float GPA;
string line;
public:
void binary_write(std::ostream& out) const;
void binary_read(std::istream& input);
};
void Student::binary_write(std::ostream& out) const
{
out.write(&id, sizeof(id));
std::string::size_type length(name.length());
out.write(&length, sizeof(length));
out.write(name.data(), length);
out.write(&GPA, sizeof(GPA));
length = line.length();
out.write(line.data(), length);
}
void Student::binary_read(std::istream& input)
{
input.read(&id, sizeof(id));
std::string::size_type length;
input.read(&length, sizeof(length));
char * temp = new char[length];
input.read(temp, length);
name = std::string(temp);
input.read(&GPA, sizeof(GPA));
delete[] temp;
input.read(&length, sizeof(length));
temp = new char[length];
input.read(temp, length);
line = std::string(temp);
delete [] temp;
}
The above code uses the technique of writing the length of the string, followed by the string contents. The reading consists of using a temporary buffer, then reads the text into the buffer and creates a std::string from the buffer.
There are other methods to read in a string (search your C++ reference for "c++ string input iterator), but this illustrates the point.
The problem with writing pointers, and std::string contains pointers, is that there is no guarantee that the operating system will load your program into the exact same place in memory at every invocation.
The binary_write function is declared as const since it doesn't change any of the Student data members. This technique is called "const correctness". Search the internet for its definition.
The default accessibility for class is private, so I removed the private from the section declaring the variables.
Related
class Client
{
public:
Client(int id, string title, int age):
~Client();
void addTW(int id, string title, int age);
int getID() const {return id;}
string getTitle() const {return title;}
int getAge() const {return age;}
private:
int id;
string title;
int age;
};
I have two functions:
load(), which is loading input .txt file - file's having titles of movies and age you need to have in order to watch the movie (e.x. Pulp Fiction - 16) and
addTW(int id, string title, int age), which adds Movies.
So, while adding a movie, you need to type id, title and age. I want to make that you cannot add movie if you're under a certain age (e.x. 16 or whatever). Age must be re-added from the .txt file. Basically age in connected with and only title.
I've never used .txt files. So I have no idea how to start.
#include <fstream>
void Client::addTW(int id, string title, int age)
{
int i, n = tw.size();
for(i = 0;i<n;i++)
{
ToWatch* newTW = new ToWatch(id, title, age);
tw.push_back(newTW);
return;
}
}
void Client::load()
{
ifstream input;
input.open("input.txt");
if(input.fail())
{ cout<<"Failure"<<endl;}
else
{
string s;
while(input>>s)
{
cout<<s<<" ";
}
}
input.close();
}
I am not sure, if the design of your class is OK. This you can find out by yourself.
I can help you with reading the file and extracting the age for a given title:
Please see:
#include <iostream>
#include <fstream>
#include <string>
unsigned int getAgeFromFile(const std::string& title) {
// We set a default age of 0. So, if we cannot find the title in the list, then everybody can look it
unsigned int resultingAge{ 0 };
// Define an ifstream variable. Use its constructor, to open the file, then check, if open was ok
if (std::ifstream fileMovies("input.txt"); fileMovies) {
// Read all lines in the text file in a loop with std::getline. std::getline will return false,
// if we are at end-of-file or in case of some other error. Then the loop will stop
for (std::string line{}; std::getline(fileMovies, line); ) {
// So, now we have a line from the file in tour "line" variable.
// Check, if the searched title is in it
if (line.find(title) != std::string::npos) {
// Ok, we found the title in this line. Now, we need to extract the age.
// It is at the end of the line and separated by a space. So, search from the end of the line for a space
if (size_t pos{ line.rfind(' ') }; pos != std::string::npos) {
// We found a space. Now, convert the number.
resultingAge = std::stoul(line.substr(pos));
}
}
}
}
// return result or default value, if not found
return resultingAge;
}
In your addTW function you need to insert one line before the
push_back.
if (age > getAgeFromFile(title))
Hope this helps.
Compiled and tested with VS2019 and C++17
I do not understand why my array of pointers is only saving the last line from the file that I am reading from. When I substitute a string literal into the setData() function the code works just fine. All that the "mann" file contains are a bunch of words order alphabetically. Thank you.
#include <iostream>
#include <fstream>
using namespace std;
class orignialData {
char* data;
public:
void setData(char* s) { data = s;}
char* getData() const {return data;}
};
class dataClass {
orignialData** W_;
public:
dataClass(char* filename);
void addData();
void viewAll();
};
dataClass::dataClass(char* filename) {
fstream file;
file.open(filename, ios::in);
if (file.fail()) {
cout << "There was an error reading the file...\n";
}
W_ = 0;
W_ = new orignialData*[5];
for (int i = 0; i < 5; i++)
W_[i] = new orignialData;
char buff[30];
char* temp;
while(file >> buff) {
cout << buff << endl;
static int i = 0;
W_[i] -> setData(buff);
i++;
}
file.close();
}
Instead of data = s, write data = strdup(s) to make a copy of the contents. Otherwise, you will assign the same pointer again and again, and you will overwrite the contents of the memory to which this pointer points again and again. At the end, your temporary buffer will contain the last line of your file, and all the pointers will point to exactly this buffer. That's what you are observing...
I'm writing a C++ program that reads in a list of passengers and creates Passenger objects that are stored in a dynamically allocated pointer. However, I noticed that when I ran the program, adding a new passenger would cause all of the previously set passengers to be assigned to the new passenger too.
The likely problematic segment of the code below:
int x = 400;
passengerslot = 0;
Passenger * list = new Passenger[x];
ifstream myfile("Airline.CSV");
if (myfile.is_open())
{
while (getline(myfile, line))
{
std::istringstream ss(line);
std::string token;
int dataslot = 0;
while (std::getline(ss, token, ',')) {
switch (dataslot){
case 0:
*last = token;
break;
case 1:
*first = token;
break;
case 2:
*rownum = atoi(token.c_str());
break;
case 3:
*seat = token.at(0);
break;
case 4:
*flightnum = atoi(token.c_str());
list[passengerslot] = *new Passenger(last, first, rownum, flightnum, seat);
cout << list[passengerslot].getPassenger() << endl; //prints as the passengers are assigned
if (passengerslot != 0){
cout << list[passengerslot - 1].getPassenger() << endl;
}
My passenger constructor:
Passenger::Passenger(string *fname, string *lname, int *rownum, int *flightnum, char *seatchar):firstname(fname), lastname(lname), rownumber(rownum), flightnumber(flightnum), seat(seatchar){
*firstname = *fname;
*lastname = *lname;
*rownumber = *rownum;
*flightnumber = *flightnum;
*seat = *seatchar;
}
The unusual dereferencing of pretty much everything has me worried that you may be reusing the same pointers over and over, writing over the same memory locations every time, then assigning those memory locations to pointers inside the Passenger.
For example, the constructor:
Passenger::Passenger(string *fname, string *lname, int *rownum, int *flightnum, char *seatchar):firstname(fname), lastname(lname), rownumber(rownum), flightnumber(flightnum), seat(seatchar)
{
*firstname = *fname;
*lastname = *lname;
*rownumber = *rownum;
*flightnumber = *flightnum;
*seat = *seatchar;
}
firstname(fname) means that what has to be a pointer to string, if this program compiles, firstname is being assigned the address pointed to by fname. The same pattern follows for the other members and associated parameter.
*firstname = *fname; does effectively nothing. It copies the string at fname over top of the string at firstname, which because of the assignment in the previous point are the exact same place.
This means that all of the Passenger::firstnames point to fname, all Passenger::lastnames point to the lname, etc... The end result is that all Passengers look exactly the same as the most recent Passenger.
Solution:
Stop using pointers for everything. You probably don't need any pointers.
The accumulation variables should be:
std::string first;
std::string last;
int rownum;
int flightnum;
char seat;
Passenger should be:
class Passenger
{
public:
Passenger(const std::string & fname,
const std::string & lname,
int rownum,
int flightnum,
char seatchar);
// other unspecified public methods
private:
std::string firstname;
std::string lastname;
int rownumber;
int flightnumber;
char seat;
// other unspecified private data and methods
}
and the constructor
Passenger::Passenger(const std::string & fname,
const std::string & lname,
int rownum,
int flightnum,
char seatchar):
firstname(fname), lastname(lname), rownumber(rownum),
flightnumber(flightnum), seat(seatchar)
{
}
Storage definition of
Passenger * list = new Passenger[x];
should be more along the lines of
std::vector<Passenger> list;
std::vector is a dynamically sizing array with all of the memory management required for most use cases built right in.
And rather than loading list with
list[passengerslot] = *new Passenger(last, first, rownum, flightnum, seat);
use
list.emplace_back(first, last, rownum, flightnum, seat);
I am writing a c++ program to write data into a file in binary mode and read it from the file.
I am writing an object and reading to an object.
The problem I am facing is, when I write into the file and read it in that instance without closing the program, it works file. But after the program terminates execution, and comment out the writing code block and try to read the already written file, I get crappy output.
I am not able to understand what is going wrong.
Here is the code:
#include <fstream.h>
#include <string.h>
class Student{
protected:
char *Name, *Sub_code;
int Roll;
public:
Student(){}
};
class Details:private Student{
private:
char *Sub_Name;
int internal_marks, external_marks;
/*Methods*/
void setName();
void setRoll();
void setSubCode();
void setSubName();
void setInternalMarks();
void setExternalMarks();
public:
Details(){
Name = new char[1];
Sub_code = new char[1];
Sub_Name = new char[1];
Name[0] = '\0';
Sub_code[0] = '\0';
Sub_Name[0] = '\0';
internal_marks = 0;
external_marks = 0;
Roll = 0;
}
void setDetails();
void getDetails();
static void writeDetails(Details detail);
static void readDetails();
};
void Details::setName(){
cout<<"Enter Student Name : ";
char tmp[100];
tmp[0] = '\0';
cin>>tmp;
int len = strlen(tmp);
Name = new char[len];
strcpy(Name,tmp);
}
void Details::setRoll(){
cout<<"Enter Roll Number : ";
cin>>Roll;
}
void Details::setSubCode(){
cout<<"Enter Subject Code : ";
char tmp[100];
tmp[0] = '\0';
cin>>tmp;
int len = strlen(tmp);
Sub_code = new char[len];
strcpy(Sub_code,tmp);
}
void Details::setSubName(){
cout<<"Enter Subject Name : ";
char tmp[100];
tmp[0] = '\0';
cin>>tmp;
int len = strlen(tmp);
Sub_Name = new char[len];
strcpy(Sub_Name,tmp);
}
void Details::setInternalMarks(){
cout<<"Enter internal marks : ";
cin>>internal_marks;
}
void Details::setExternalMarks(){
cout<<"Enter external marks : ";
cin>>external_marks;
}
void Details::setDetails(){
setName();
setRoll();
setSubCode();
setSubName();
setInternalMarks();
setExternalMarks();
}
void Details::getDetails(){
cout<<Name<<"\t\t";
cout<<Roll<<"\t\t";
cout<<Sub_code<<"\t\t";
cout<<Sub_Name<<"\t";
cout<<internal_marks<<"\t";
cout<<external_marks<<"\t\n";
}
void Details::writeDetails(Details detail){
ofstream os("StudentsRecord.dat", ios::binary|ios::ate);
os.write(reinterpret_cast <char *>(&detail),sizeof(detail));
os.close();
}
void Details::readDetails(){
Details detail;
ifstream is("StudentsRecord.dat", ios::binary|ios::in|ios::beg);
cout<<"Name\tRoll\tSubject Code\tSubject Name\tInternal marks\tExternal Marks\n";
while (is.read(reinterpret_cast<char *>(&detail), sizeof(detail))){
detail.getDetails();
}
is.close();
}
int main(){
Details y,x;
/*for(int i = 0; i < 2; i++){
x.setDetails();
Details::writeDetails(x);
}*/
Details::readDetails();
return 0;
}
The commented code in the main() is the block that is used for writing the data in the file.
Here is the sample screen shot of the output I am getting.
Regards Priyabrata
The Detail class has char* inside. When you write it to the file you write the actual pointer address of the string, not the actual string. When you read it back in the same program it works because the data is still there.
When you rerun your program you get garbage because that pointer is not at some random piece of memory.
You should use a library that does serialization for you. It's kinda hard to get it right.
Take a look at https://code.google.com/p/protobuf/ , but I'm sure there are other ones as well.
So I have a custom class 'Book' that has a bunch of member variables, amongst which are a vector of another custom class called 'Review' and a pointer to point at that vector as I need to pass that around through function calls in a driver program. The driver program reads the details of the each book (such as title, author, publish date etc.) from a text file and inserts into a temporary 'Book' object which it then adds onto a vector of Books maintained by the driver program. Here is the code for reading from the file:
ifstream file("books.txt");
string line;
if(file.is_open())
{
while(!file.eof())
{
Book buffBook;
getline(file, line);
buffBook.setTitle(line);
getline(file, line);
buffBook.setAuthor(line);
getline(file, line);
buffBook.setPubDate(line);
getline(file, line);
buffBook.setIsbn(line);
getline(file, line);
buffBook.setCategory(line);
getline(file, line);
buffBook.setFormat(line);
getline(file, line);
buffBook.setSynopsis(line);
vectBooks.push_back(buffBook);
}
}
else
cout<<"File not found(1)!"<<endl;
file.close();
This is running inside a int main() function.
One of the functions of the driver program is to add a review, which takes in data from the user and inserts it into a temporary 'Review' object. That object is then passed onto to be inserted in to the vector of reviews for the corresponding book. Here is the code for the addReview() function:
void addReview()
{
string name = "";
string title;
Book rTemp;
cin.ignore();
cout<<"Which book would you like to rate (Title)?: ";
getline(cin, name);
name = toLow(name);
Review r;
string re, user;
int ra;
cout<<"Username (Full Name): ";
getline(cin, user);
string fname = user.substr(0, user.find_first_of(' '));
string lname = user.substr( user.find_first_of(' ') + 1, user.size());
r.setUsrFName(fname);
r.setUsrLName(lname);
cout<<"Enter rating (1-5):";
cin>>ra;
r.setRating(ra);
cout<<"Enter a short textual review: ";
cin.ignore();
getline(cin, re);
r.setReview(re);
for(unsigned int i = 0; i < vectBooks.size(); i++)
{
title = toLow(vectBooks[i].getTitle());
if(title.find(name) != string::npos)
{
vectBooks[i].getReviews()->push_back(r);
}
}
}
Now the problem is if i add a review, it adds it for all the books. In other words, when I fetch the book info for any book, the review shows on all the books. I assume this is a problem with the pointer as it seems like all the reviews are getting stored in the same vector. I am not sure where I am messing up but I have a feeling it's with the pointer some where. Any help is appreciated.
Thank You
UPDATE
The point to the title of this problem is that I am doing the assignment of the pointer to the vector of Reviews in the constructor of the Book class, of which those 2 are member variables. Code as follows for constructor:
Book::Book()
{
pointRev = &vectReviews;
}
UPDATE 2
Here is the code for the Book Class and supporting classes:
book.h
#ifndef BOOK_H_
#define BOOK_H_
#include <string>
#include <iostream>
#include <vector>
#include "review.h"
using namespace std;
class Book
{
private:
string title;
string author;
string pubDate;
string isbn;
string category;
string format;
string synopsis;
vector<Review> vectReviews;
vector<Review>* pointRev;
public:
Book::Book() : pointRev(&vectReviews) {};
string getAuthor() const;
string getCategory() const;
string getFormat() const;
string getIsbn() const;
string getPubDate() const;
string getSynopsis() const;
string getTitle() const;
vector<Review>* getReviews();
void setAuthor(string author);
void setCategory(string category);
void setFormat(string format);
void setIsbn(string isbn);
void setPubDate(string pubDate);
void setSynopsis(string synopsis);
void setTitle(string title);
friend ostream& operator <<(ostream& out, Book& book);
vector<Review> *getPointRev() const;
vector<Review> getVectReviews() const;
void setPointRev(vector<Review> *pointRev);
void setVectReviews(vector<Review> vectReviews);
};
#endif /* BOOK_H_ */
book. cpp
#include "book.h"
string Book::getAuthor() const
{
return author;
}
string Book::getCategory() const
{
return category;
}
string Book::getFormat() const
{
return format;
}
string Book::getIsbn() const
{
return isbn;
}
string Book::getPubDate() const
{
return pubDate;
}
string Book::getSynopsis() const
{
return synopsis;
}
string Book::getTitle() const
{
return title;
}
void Book::setAuthor(string author)
{
this->author = author;
}
void Book::setCategory(string category)
{
this->category = category;
}
void Book::setFormat(string format)
{
this->format = format;
}
void Book::setIsbn(string isbn)
{
this->isbn = isbn;
}
void Book::setPubDate(string pubDate)
{
this->pubDate = pubDate;
}
void Book::setSynopsis(string synopsis)
{
this->synopsis = synopsis;
}
void Book::setTitle(string title)
{
this->title = title;
}
vector<Review> *Book::getPointRev() const
{
return pointRev;
}
vector<Review> Book::getVectReviews() const
{
return vectReviews;
}
void Book::setPointRev(vector<Review> *pointRev)
{
this->pointRev = pointRev;
}
void Book::setVectReviews(vector<Review> vectReviews)
{
this->vectReviews = vectReviews;
}
vector<Review>* Book::getReviews()
{
return pointRev;
}
ostream& operator <<(ostream& out, Book& book)
{
out<<"\nTitle: "<<book.getTitle()<<endl;
out<<"Author: "<<book.getAuthor()<<endl;
out<<"Publish Date: "<<book.getPubDate()<<endl;
out<<"ISBN: "<<book.getIsbn()<<endl;
out<<"Category: "<<book.getCategory()<<endl;
out<<"Format: "<<book.getFormat()<<endl;
out<<"Synopsis: "<<book.getSynopsis()<<endl;
cout<<"\n--- Reviews ---"<<endl;
// vector<Review>* revs = book.getReviews();
for(unsigned int h = 0; h < book.getReviews()->size(); h++)
{
cout<<"Review by: "<<book.getReviews()->at(h).getUsrFName()<<" "<<book.getReviews()->at(h).getUsrLName()<<endl;
cout<<"Rating: "<<book.getReviews()->at(h).getRating()<<endl;
cout<<"Review: "<<book.getReviews()->at(h).getReview()<<endl;
}
return out;
}
review.h
#ifndef REVIEW_H_
#define REVIEW_H_
#include <string>
using namespace std;
class Review
{
private:
int rating;
string review;
string usrFName;
string usrLName;
public:
int getRating() const;
string getReview() const;
void setRating(int rating);
void setReview(string review);
string getUsrFName() const;
string getUsrLName() const;
void setUsrFName(string usrFName);
void setUsrLName(string usrLName);
};
#endif /* REVIEW_H_ */
review.cpp
#include "review.h"
int Review::getRating() const
{
return rating;
}
string Review::getReview() const
{
return review;
}
void Review::setRating(int rating)
{
this->rating = rating;
}
string Review::getUsrFName() const
{
return usrFName;
}
string Review::getUsrLName() const
{
return usrLName;
}
void Review::setUsrFName(string usrFName)
{
this->usrFName = usrFName;
}
void Review::setUsrLName(string usrLName)
{
this->usrLName = usrLName;
}
void Review::setReview(string review)
{
this->review = review;
}
From the behavior you describe, a copy-constructor is running and making two objects that point to the same vector. push_back does indeed use a copy-constructor.
But your first code snippet doesn't make a bunch of copies of the same Book, but a new Book is created on each loop iteration (and then copied into vectBooks.
If Book doesn't have a correct user-defined copy-constructor then you aren't managing pointRev correctly. From the observed behavior, I believe that you have a destructor which frees pointRev, and then the copy inside vectBooks is left with a dangling pointer. Everything after this falls into the category of undefined behavior according to the standard, meaning "anything can happen" Then the next Book happens to reuse the same memory area, so all the instances of Book end up with the same value in the wild pointer. And then updating any one changes the vector (which isn't even alive anymore) seen by all Book instances.
Why are you using a pointer to a std::vector anyway? Much better to just put the vector into the class as a direct data member, which lets the compiler construct, copy, and destruct it automatically with no additional help from you.
Of course, it's entirely possible that you've made vectReviews a global variable, and then every single book points to the same instance. That would a review show up in all books simultaneously, because they share the vector you're adding it to.