Simple File processing in C++ - c++

Trying to read a record of students with their name and numbers.
Writing to the file seems fine. However, reading from it prints a never-ending output. The statement - while(!file.eof()) - is causing the problem. But it's how I read the remaining person_details. Your help would be greatly appreciated.
#include <iostream>
#include <fstream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::string;
using std::ios;
class telephone
{
protected:
string name;
int number;
public:
void getDetails();
void printDetails() const;
};
void telephone:: getDetails()
{
cout<<"Enter name : "; getline(cin,name);
cout<<"Enter number : ";cin>>number;
}
void telephone:: printDetails() const
{
cout<<"Name : "<<name<<endl;
cout<<"Number : "<<number<<endl;
}
int main(int argc, char const *argv[])
{
telephone person;
fstream file("telefile.txt",ios::in | ios::out | ios::binary | ios::app);
if (!file)
{
cout<<"Invalid file name."<<endl;
return 1;
}
//writing
char choice;
do{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.getDetails();
file.write(reinterpret_cast<char*>(&person),sizeof(person));
cout<<"Enter one more?";
cin>>choice;cin.ignore();
}while(choice == 'y');
//reading
file.seekg(0);
file.read(reinterpret_cast<char*>(&person),sizeof(person));
while(!file.eof())
{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.printDetails();
file.read(reinterpret_cast<char*>(&person),sizeof(person));
}
return 0;
}

Your basic problem is that you're making a binary image of a std::string object, but a string object doesn't contain the character data, only a pointer to it (this is what allows a string to vary in size). So when you read that pointer back in, you'll start accessing whatever is in that memory location now, not what that memory held when you wrote the file. oops. Even worse things will happen when the string destructor runs and tries to free that pointer.
What you should do instead is write person.name.size() to the file, followed by that many bytes starting at &person.name[0]. Then when you read in the size, you can person.name.resize(size_from_file) and then read that many bytes into &person.name[0].
The Standard actually has a formal name for data types you are allowed to take binary images of: trivially copyable. The requirements are set out in section 9 and your telephone type doesn't meet them:
A trivially copyable class is a class that:
has no non-trivial copy constructors,
has no non-trivial move constructors,
has no non-trivial copy assignment operators,
has no non-trivial move assignment operators, and
has a trivial destructor.
The compiler-generated special member functions for telephone are non-trivial because of the std::string member, so instead of meeting all five requirements, you don't meet any of them.
Everyone has mentioned while (!file.eof()). You're not actually doing it wrong, but there is room for improvement.
First, you can catch more errors using while (file.good()) instead. Right now if you have any failure other than EOF, your loop never terminates, which matches your symptom.
Next, streams have a conversion to bool which is equivalent to calling good(), so you can write while (file). Finally, read like most other stream I/O functions returns the original stream, so you can write
while (file.read(buffer, size))
and avoid duplicating the read call both above and inside the loop.
But you didn't fall into the very common trap of checking eof() before doing the read that actually ran into the end. Bravo for that.

Related

Reading/Writing to a file in c++

I am trying to reading and write objects to a file in C++, writing the object works fine, reading gives segmentation core dump. I have commented the code for writing objects to file, while writing we can uncomment that part and comment the reading part.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class RelianceMart{
string name;
double trolley_number;
public:
RelianceMart(){
name = "NA";
trolley_number = 0;
}
RelianceMart(string name, double trolley_number){
this->name = name;
this->trolley_number = trolley_number;
}
void setname(string name){
this->name = name;
}
string getname(){
return name;
}
void settrolleynumber(double trolley_number){
this->trolley_number = trolley_number;
}
double gettrolleynumber(){
return trolley_number;
}
};
int main(){
string name;
double trl_num;
RelianceMart mart[3];
RelianceMart obj;
// ofstream fout("PersistentStorage.txt");
/*
for(int i=0;i<3;i++){
cin>>name;
cin>>trl_num;
mart[i] = RelianceMart(name, trl_num);
fout.write((char *) & mart[i], sizeof(mart[i]));
}
fout.close();
*/
ifstream fin("PersistentStorage.txt");
while(!fin.eof()){
fin.read((char *) & obj,sizeof(obj));
cout<< obj.getname();
}
fin.close();
return 0;
}
The members of std::string is really nothing more than a member variable for the length, and a member variable being a pointer to the actual string contents.
Pointers are private and unique to a specific process in all modern protected multi-tasking operating systems, no other process (not even one started from the same program) can reuse the same pointer.
When you write the RelianceMart objects, you write the pointer of the name string object to the file. As mentioned above no other process can use this pointer, and therefore can't read the file.
Furthermore when you attempt to read the raw objects, you read raw data overwriting the existing data in the constructed object, and the object won't be properly constructed anymore.
You also don't open the file in binary mode, which is wrong since you write and read raw binary data, not text.
The common solution is to use serialization, and the most common way to do it is simply to overload the "output" and "input" operators << and >>.
In the overloaded functions you simply write and read each object as text, again using the formatted << and >> operators.
Lastly, please read Why is iostream::eof inside a loop condition considered wrong?
I would use a serialization framework, you could use Google's Protocol Buffers(https://developers.google.com/protocol-buffers/).
If you consider a fullblown framework overkill, you can always write your own serialization framework, I've done that, I did use the JSON-format to encode the object.

c++ weird characters appear while writing to a file

When i run this code, and open the .dat file in notepad/wordpad it shows some weird characters
#include<iostream>
#include<fstream>
using namespace std;
class Student
{
int rollno;
char name[20];
char div[20];
public:
void accept()
{
cout<<"enter rollno"<<endl;
cin>>rollno;
cout<<"Enter name"<<endl;
cin>>name;
cout<<"Enter div"<<endl;
cin>>div;
}
void write_rec()
{
ofstream f;
f.open("Student.dat",ios::binary|ios::app);
Student s;
s.accept();
f.write((char*)&s,sizeof(s));
f.close();
}
void display()
{
ifstream f;
Student s;
f.open("Student.dat",ios::binary|ios::in);
while(f.read((char*)&s,sizeof(s)))
s.show();
f.close();
}
void show()
{
cout<<rollno<<endl;
cout<<name<<endl;
cout<<div<<endl;
}
};
int main()
{
Student s;
s.write_rec();
s.display();
}
The code is getting compiled and run perfectly but when i open the "Student.dat" file it shows some weird characters
This line:
f.write((char*)&s,sizeof(s));
Is writing the raw binary bytes of your Student object into the file. If that's not the behavior you intended, you'll need to do something else, e.g. translate the member fields of your Student object (in particular the rollno variable, which is an int) into ASCII text strings and write those strings into the file instead.
on both places when you open file:
f.open("Student.dat",ios::binary|ios::app);
and:
f.open("Student.dat",ios::binary|ios::in);
you open as binary. try to remove this option.
as you can see here and here, these are platform specific behaviors.
take a look at this example
EDIT:
and, of course, as Jeremy observed, you have to write meaningful text to your file. on this line:
f.write((char*)&s,sizeof(s));
you are actually writing each byte (as (char*)) of your object. if you understand what you're doing, and that's what you want to observe, than ok. but maybe you should compare both outputs, from this method and from Jeremy's suggestion.

How do I read from an input file after passing the ifstream object to a function?

as the title suggests, I am having a problem with not being able to read from an input file after passing the ifstream object to a class function. Basically I'm trying to sort a list of numbers using a heap ADT implemented with an array.
int main() {
ifstream infile("input.txt");
HeapSort* heap = new HeapSort(20); // creates a heap (array) with size 20
heap->buildHeap(&infile);
return 0;
}
void HeapSort::buildHeap(ifstream* infile) {
int data;
while (infile >> data) {cout << data << endl;}
infile->close();
}
the error occurs in the conditional of the while loop inside buildHeap. The compiler can't recognize the operator ">>" between an 'int' and an 'ifstream' object. However, strangely enough, if I write that same while loop inside main(), it'll work just fine. Also of note is that if I remove the while loop, the compiler returns no errors. Meaning, simply the act of passing the ifstream object from main to buildHeap is OK.
Please avoid suggesting alternative ways of achieving this. I was asked to not use any special fstream functions like eof(). I can only use the ">>" operator to read from the desired file.
You're passing a pointer to a stream, so you need to dereference it:
while (*infile >> data)
If you want your code to look like what you say you did in main, then you pass a reference:
heap->buildHeap(infile);
//...
void HeapSort::buildHeap(ifstream& infile)
{
int data;
while (infile >> data) { ... }
infile.close();
}

File handling in C++ gives .exe stopped working error

I have basic file handling code of reading file named"student.dat" using visual studio.
the output reads the file and displays the result in console but visual studio pops up the dialog as
code:
#include<iostream>
#include<fstream>
#include<conio.h>
#include<string>
using namespace std;
class student
{
int admno;
// char name[20];
string name;
public:
void getdata()
{
cout<<"\n\nEnter The Name of The Student ";
//gets(name);
cin>>name;
getch();
cout<<"\nEnter The admission no. ";
cin>>admno;
// getch();
}
void showdata()
{
cout<<"\nAdmission no. : "<<admno;
cout<<"\nStudent Name : ";
cout<<name;
//puts(name);
}
int retadmno()
{
return admno;
}
};
int main()
{
student obj;
ifstream fp1;
fp1.open("student.dat",ios::binary);
while(fp1.read((char*)&obj,sizeof(obj)))
{
obj.showdata();
}
fp1.close();
return 0;
}
You are allowed to load raw data only to some POD objects. This line is an error:
fp1.read( (char*)&obj,sizeof(obj) );
because student contains std::string. std::string contains a pointer to a memory block which becomes invalid and totally useless after the object is destroyed. It means the data you load to std::string is just a garbage.
I'd think about object serialization. Boost serialization is a good way to start.
You try to store/load an object to a file, which is invalid.
while(fp1.read((char*)&obj,sizeof(obj)))
Objects may contain references to memory (std::string e.g.) that are invalid after loading. You have to provide serialization for your objects or use plain structs without pointers or references to store your data. E.g.:
struct {
int admno;
char[20] name;
}
If you need strings with variable length, you may use a string store:
struct {
int admno;
size_t name_index;
}
std::vector<std::string> names;
To store, iterate over your names and save length and .c_str of each std::string. To read, do it the other way around and read length into n, read n bytes into a buffer and construct a std::string out of the buffer.
You might have a look at http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html
You never call GetData so admno is not instantiated in ShowData

SIGABRT in binary read/write

I wrote a very small code snippet and have already gotten the following error:
malloc: *** error for object 0x100100080: pointer being freed was not allocated
Problem is, I have no idea what pointer the compiler's talking about. I pass a variable in by address to the read/write functions, but I never freed it as far as I know. Where's the error in my code? I ran it with Leaks and Zombies, but got nothing.
Here's my program:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Bank
{
private:
string __name;
public:
Bank()
{
__name = "";
}
Bank(string name)
{
__name = name;
}
string getName() const { return __name; }
};
int main (int argc, char * const argv[])
{
Bank bank("Bank of America");
Bank bank2;
cout << "Bank1: " << bank.getName() << endl;
string filename = bank.getName() + ".bank";
ofstream fout(filename.c_str(), ios::binary);
if (fout.good())
fout.write((char *)&bank, sizeof(bank));
fout.close();
ifstream fin(filename.c_str(), ios::binary);
if (fin.good())
fin.read((char *)&bank2, sizeof(bank2));
fin.close();
cout << "Bank2: " << bank2.getName() << endl;
return 0;
}
You can't read an object that contains a std::string (or anything that's not Plain Ol' Data) with fin.read()--
The object is read and written as a stream of bytes, but std:string contains a pointer to memory that is stored elsewhere and is not written with your fout.write() and is not initialized properly with your fin.read()
It is because it is not initialized properly with your fin.read() that you are getting the heap error; when the object goes out of scope, the destructor of the improperly initialized std::string is being called, and trying to free memory that it doesn't own.
You probably want to write a custom i/o method for your object and save or load it piece-by-piece. For a shortcut to doing this, use the Boost serialization library.
Because your Bank class contains a std::string, you can't read/write it as binary like you are thinking. A std::string has internal pointers. If you write it as binary, you are just going to be writing pointers and not the actual string contents. Likewise, when you read the string, you are going to be reading a pointer. In this case, you end up making both your bank and bank2 objects have strings which point to the same memory, so when that memory is freed it gets freed twice.
You'll need to have some other way of writing your bank data to a file. In this case, a simple ASCII file with the bank name would be fine.
You cannot do what you are doing, simply because std::string cannot be copied like that. Internally a string object allocates memory and a simple copy of the outer structure doesn't do what you expect.
You need to serialize this structure properly.
Don't use underscores, please
Pass objects by reference: Bank(string& name), please
This is evil: fout.write((char *)&bank, sizeof(bank));
You may want to write << and >> ostream operators of your Bank class.
For example:
friend std::ostream& operator<<(std::ostream &out, const Bank& b);
friend std::istream& operator>>(std::istream &out, const Bank& b);
Members functions write of ostream and read of istream are specifically designed to input and output binary data. If you do want to manipulate binary data, use the following:
ifstream fin(filename.c_str(), ios::in|ios::binary|ios::ate);
size = fin.tellg();
memblock = new char [size];
fin.seekg(0, ios::beg);
if (fin.good()){
fin.read(memblock, size);
fin.close();
}
delete[] memblock;