Writing and reading objects to and from file - c++

I have a class User with two std::string attributes. When i try to read it from file i got exception from uxitility from line 222:
(*_Pnext)->_Myproxy = nullptr;
It happens after function isUserInFile()
Some peaces of my code:
Class User {
protected:
std::string login;
std::string password;
public:
friend std::istream&operator>>(std::istream&in, User &obj) {
//std::cout << "Логин: "; //"Login: "
in >> obj.login;
//std::cout << "Пароль: "; //"Password: "
in >> obj.password;
return in;
}
friend std::ostream&operator<<(std::ostream&out, User&obj) {
out << obj.login << " " << obj.password;
return out;
}
void encrypt() {
char key[3] = { 'K','E','Y' };
std::string buf = password;
for (int i = 0; i < buf.size(); i++)
buf[i] = password[i] ^ key[i % (sizeof(key) / sizeof(char))];
password = buf;
//buf.clear();
}
bool isUserInFile() {
User user;
std::ifstream file;
file.open("Users.txt");
while (!file.eof() && file.read((char*)&user, sizeof(User)))
if (login == user.login) {
file.close();
return true;
}
file.close();
return false;
}
};
bool registration() {
User user;
std::fstream file;
file.open("Users.txt", std::ios::in | std::ios::out | std::ios::app);
std::cout << "Регистрация:\n"; //"Registration:\n"
std::cin >> user;
user.encrypt();
if (!user.isUserInFile()) {
file.write((char*)&user, sizeof(User));
file.close();
return true;
}
std::cout << "Пользователь с данным логином уже существует\n"; //"User with this login already exists\n"
file.close();
system("pause");
return false;
}

Comments went in the correct direction to show where the problem is. But the solution is much simpler, you already have your stream operators, so just use them!
To write into the file you can use:
file << user << std::endl;
and then to read you simply:
file >> user;
For this to keep working you will need some things:
A user should never enter a white-space anywhere in their password.
You need to ensure that the writing and reading is always done in the same order.
Alternatively you can create a conversion from string and to string along the lines of:
static const char SEP1 = ' ', SEP2 = '\r';
friend std::string to_string(const User& u)
{
std::string result = u.login + SEP1 + u.password + SEP2;
return result;
}
explicit User(std::string line)
{
size_t pos1 = s.find(SEP1);
size_t pos2 = s.find(SEP2, pos1);
login = s.substr(0, pos1);
password = s.substr(pos1+1, pos2-pos1);
}
Then you can in your main you can read a block of data and simply construct a user from it, alternatively you can convert a user into a string before writing. A beauty of this approach is that you select the separators and they are stable between functions.

Related

Runtime termination when adding Block class instance to vector using push_back

I'm writing a project to simulate (an oversimplified version of) a blockchain. I've written a class called Block to manage blocks in the blockchain:
(You may notice that I am referencing some additional classes/headers which are not listed in an #include statement at the top. To my knowledge, all the dependencies are adequately taken care of by the makefile.)
#include "Block.h"
/**** constructors ****/
Block::Block()
{
prevHash = "";
merkleRoot = "";
nonce = "";
}
Block::Block(std::string str)
{
std::vector<std::string> strings;
std::string word;
int pos = str.find(" ");
while(pos != std::string::npos)
{
word = str.substr(0, pos);
strings.push_back(word);
pos = str.find(" ");
str = str.substr(pos+1);
}
if(strings.size() != 3)
{
prevHash = "";
merkleRoot = "";
nonce = "";
}
else
{
prevHash = strings.at(0);
merkleRoot = strings.at(1);
nonce = strings.at(2);
}
}
Block::Block(std::vector<Block> &blocks, std::string merkle)
{
// use prev block from Block vector to get hash of prev block
Block prevBlock = blocks.at(0);
prevHash = picosha2::hash256_hex_string(utils::hexToString(prevBlock.toString()));
merkleRoot = merkle;
nonce = mine();
}
Block::Block(std::string hash, std::string merkle, std::string n)
{
prevHash = hash;
merkleRoot = merkle;
nonce = n;
}
Block::Block(const Block &b) //copy constructor
{
prevHash = b.getPrevHash();
merkleRoot = b.getMerkleRoot();
nonce = b.mine();
}
Block::~Block() //destructor
{
}
/**** public methods ****/
bool Block::isValid()
{
std::string hash = picosha2::hash256_hex_string(utils::hexToString(this->toString()));
if(hash[0] == '0')
return true;
return false;
}
std::string Block::toString()
{
return prevHash + merkleRoot + nonce;
}
std::string Block::mine() const
{
// brute force nonces so hash begins with zero/block is valid
for(int i = 0; i < pow(2, 8); i++)
{
Block temp(this->getPrevHash(), this->getMerkleRoot(), std::to_string(i));
if(temp.isValid())
{
// convert i to hex string and return
std::stringstream stream;
stream << std::hex << std::to_string(i);
return stream.str();
}
}
std::cout << "No nonce found." << std::endl;
return "";
}
// calculate this block's hash
std::string Block::calcHash()
{
std::string hash = picosha2::hash256_hex_string(utils::hexToString(this->toString()));
return hash;
}
std::string Block::getPrevHash() const
{
return prevHash;
}
std::string Block::getMerkleRoot() const
{
return merkleRoot;
}
std::string Block::getNonce() const
{
return nonce;
}
So, the actual problem: I can't add instances of the Block class to my vector of Blocks (or an array of Blocks, for that matter; I tried that too). I can create a Block, print it out, basically anything I want. But if I add it to a vector, my computer throws a runtime error. Below is my main file and the output of a run:
#include "main.h"
// function prototype
std::string merkleRoot(std::vector<Transaction> &);
int main(int argc, char ** argv)
{
int numblocks = 0;
int numtransactions = 0;
std::vector<Block> blocks;
std::vector<Transaction> transactions;
if(argc != 3)
{
std::cout << "Usage: main [blockchain.txt] [transaction.txt]" << std::endl;
exit(1);
}
std::string blockchainFileName = argv[1];
std::string transactionFileName = argv[2];
std::cout << "blockchainFileName: " << blockchainFileName << std::endl;
std::cout << "transactionFileName: " << transactionFileName << std::endl;
std::ifstream blockchainFile(blockchainFileName);
std::ifstream transactionFile(transactionFileName);
if(!blockchainFile)
{
std::cout << "Cannot open " << blockchainFileName << std::endl;
exit(1);
}
if(!transactionFile)
{
std::cout << "Cannot open " << transactionFileName << std::endl;
exit(1);
}
/*** Read in blockchain ***/
std::string line;
int count = 0;
while(std::getline(blockchainFile, line))
{
std::vector<Block> temp;
std::cout << "line number " << ++count << std::endl;
Block newBlock(line);
std::cout << newBlock.toString() << std::endl;
blocks.push_back(newBlock);
}
}
Output:
$ ./main textfiles/blockchain_1.txt textfiles/transactions_1.txt
blockchainFileName: textfiles/blockchain_1.txt
transactionFileName: textfiles/transactions_1.txt
line number 1
026765a1c8235d4ac23d2582cda3b9f5c062f805540320173eb9e9148c0dc518704b42e4b11ca131b443c2b02a07ec0b45407f1b125027e3e68b86ace692445800000001
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
Abort trap: 6
blockchain_1.txt file:
026765a1c8235d4ac23d2582cda3b9f5c062f805540320173eb9e9148c0dc518 704b42e4b11ca131b443c2b02a07ec0b45407f1b125027e3e68b86ace6924458 00000001
0b53181ae351f4508363cdc3e8fb3e819fb706c4ba98a3005a980a837561074a 06aa7a8cbda7ac4351c0cae116c589c2eb0ca96cb4c90844812945cb4ffe27c5 00000019
000000000000000000ad6e90c0790e83760a9d13728c23474352a2c8c7a6e0eb 2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3 0000000f
transactions_1.txt file:
Bob Alice 5.0
Alice Bob 1.0
John Bill 2.4
Bill Alice 1.3
John Bill 2.7
Bob John 7.9
Tom Todd 4.5
Todd Bob 12.0
I've dug around for several hours now and I'm kind of out of leads. I will say that I am a little rusty with C++, particularly constructing and using my own classes. If anyone has any ideas, I'd really appreciate it! Let me know if you need any additional parts of my code.

ofstream doesn't work when the variable is an attribute

This implementation of ofstream works :
bool LinuxSysCall::addNewUser(std::string const &login, std::string const &password) {
std::ofstream out;
out.open(DATABASEPATH, std::ios::app);
if (out.is_open())
{
std::string str = login + ":" + password + "\n";
std::cout << "writing " << str << std::endl;
out << str;
return true;
}
return false;
}
//The new line is written in the file
But when I put my std::ofstream out as an attribute of LinuxSysCall, it doesn't work anymore (without trowing any exceptions):
bool LinuxSysCall::addNewUser(std::string const &login, std::string const &password) {
this->out.open(DATABASEPATH, std::ios::app);
if (this->out.is_open())
{
std::string str = login + ":" + password + "\n";
std::cout << "writing " << str << std::endl;
this->out << str;
return true;
}
return false;
}
//The new line is not written in the file
Why ?
The destructor of std::ofstream calls close. This will flush the text to the file.
If you want to use a member variable (not "attribute") you would need:
bool LinuxSysCall::addNewUser(std::string const &login,
std::string const &password) {
this->out.open(DATABASEPATH, std::ios::app);
if (this->out.is_open())
{
std::string str = login + ":" + password + "\n";
std::cout << "writing " << str << std::endl;
this->out << str;
this->out.close();
return true;
}
return false;
}
As it stands, using a member variable is much worse than using the local - however, I suspect you actually want to pass the open file around amongst many member functions. If so, you can flush the output with:
this->out << std::flush;
without closing it.

how to input and out put in a binary file in with changing cursor in c++?

i have a problem with write in file and read it back.when i open file in append mode.
and when i change the cursor of file it just mix up and dont work.
how can i write in file in hash position and read it back with hash with Order(1)?
thx for helping by the way.
struct student{
int id;
char name[20];
}st;
int hashing(char word[20])
{
int hash;
int sum=0;
int z;
sum += (int)word[0];
sum += (int)word[1];
z=(sum)%1000;
hash=z*sizeof(student);
return hash;
}
void main()
{
fstream op("d://ttest.txt",ios::app | ios::binary);
if(!op)
{
cout<<endl<<"cant open file :D in write !!!!!!:"<<endl;
getch();
}
for (int i = 0; i < 3; i++)
{
cin>>st.id>>st.name;
op.seekp(hashing(st.name),ios::beg);
op.write((char *)&st,sizeof(student));
}
op.close();
cout<<endl<<endl<<endl;
fstream op2("d://ttest.txt",ios::in | ios::binary);
if(!op2)
{
cout<<endl<<"cant open file :D in write !!!!!!:"<<endl;
getch();
}
char temp[20];
for (int i = 0; i < 3; i++)
{
st.id=0;
cin>>temp;
op2.seekp(hashing(temp),ios::beg);
op2.read((char *)&st,sizeof(student));
cout<<st.id<<" "<<st.name<<endl;
//op.seekp(hashing(st.name),ios::beg);
}
op2.close();
getch();
}
Here is an example of how you might detect if the database file is initialized and if not, do the initialization:
struct student
{
int id;
char name[20];
};
int main()
{
const std::string filename = "database.txt";
std::fstream fs;
// try to open the file
fs.open(filename, std::ios::in|std::ios::out|std::ios::binary);
if(!fs)
{
// Assume it needs initializing - create new file
std::cout << "Initializing database: " << filename << '\n';
student s;
s.id = -1; // empty
s.name[0] = '\0';
fs.clear();
fs.open(filename, std::ios::app|std::ios::binary);
if(!fs)
{
// can't recover from this
std::cout << "ERROR: initializing database: " << filename << '\n';
return 1;
}
// write 1000 empty student records
for(unsigned i = 0; i < 1000; ++i)
fs.write((char*) &s, sizeof(s));
fs.close();
// try again
fs.open(filename, std::ios::in|std::ios::out|std::ios::binary);
}
if(!fs) // still not working?
{
// not just an initialization issue, must abort
std::cout << "ERROR: opening database: " << filename << '\n';
return 1;
}
// Database open and ready for business ...
}

Program works fine in VS 2008, throws exception in VS 2010 at the very end of program

I'm writing to a file using binary/direct access. Works like a charm in 2008, but creates an exception in 2010 at runtime on line 128: Unhandled exception at 0x535dad54 (msvcp100d.dll) in HovedProsjekt.exe: 0xC0000005: Access violation writing location 0xfeeefeee.
Clicking the error takes me to xutility, where line 201: _Pnext != 0; *_Pnext = (_Pnext)->_Mynextiter) is next to be executed. I looked at microsofts "breaking changes" page with no luck. I am copy/pasting the entire code in cleartext, so no outdated libraries. I have tried all combinations of enabling and disabling _HAS_ITERATOR_DEBUGGING and _SECURE_SCL. I changed my question on advice from another user to trim my code down.
//Miniprosjekt.cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
bool sjekkForOverskriving(void);
void LagFil(void);
void LeggTil();
void skrivUtFeil();
class Feil{
friend ostream& operator<<(ostream&, Feil);
friend istream& operator>>(istream&, Feil&);
protected:
int ID;
int prioritet;
string dato;
char beskrivelse[300];
char skrevetAv[30];
public:
Feil();
Feil(int, int, string, string, string);
};
Feil::Feil(){ //Default constructor
ID = 0;
prioritet = 1;
dato = "01.01.1900";
beskrivelse[0] = ' ';
skrevetAv[0] = ' ';
}
Feil::Feil(int id, int pri, string d, string beskr, string navn){ //Constructor
ID = id;
prioritet = pri;
dato = d;
strcpy(beskrivelse, beskr.c_str());
strcpy(skrevetAv, navn.c_str());
}
ostream& operator<<(ostream& out, Feil enfeil) //Overloader << operator, allowing for: cout << Feil afeil
{
out << "Id nr: " << enfeil.ID << "\nSkrevet av: " << enfeil.skrevetAv << "\nDato: " <<
enfeil.dato << "\nPrioritet: ";
if(enfeil.prioritet == 1)out << "Lav";
else if(enfeil.prioritet == 2)out << "Middels";
else out << "Høy";
out << "\nBeskrivelse:\n" << enfeil.beskrivelse << endl;
return out;
}
istream& operator>>(istream& in, Feil& enfeil) //Overloader >> operator: allowing for cin >> Feil afeil
{
in >> enfeil.ID >> enfeil.dato >> enfeil.beskrivelse >> enfeil.skrevetAv;
return in;
}
int main(void){
LagFil();
LeggTil();
skrivUtFeil();
return 0;
}
void LagFil(void){ //Creates a file with 500 empty "feil" objects
const int MAXFEIL = 500;
Feil enfeil;
ofstream utFil;
utFil.open("FeilTeller.dat");
utFil << 0;
utFil.close();
utFil.open("Feilmeldinger.dat", ios::out | ios::binary);
for(int a = 0; a <= MAXFEIL; a++){ //Fills the file with empty "Feil" objects
utFil.write(reinterpret_cast <const char*>(&enfeil), sizeof(enfeil));
}
utFil.close();
}
void LeggTil(){
int ID;
ifstream innFil;
innFil.open("FeilTeller.dat");
innFil >> ID;
innFil.close();
fstream fil;
fil.open("FeilTeller.dat");
ID++; //Opens feilteller.dat, reads how many objects have already been created. Adds one to that to append.
fil << ID;
fil.close();
int pri = 1;
string beskrivelse = "test";
string dato = "test2";
string skrevetAv = "test3";
Feil enfeil(ID, pri, dato, beskrivelse, skrevetAv);
fil.open("Feilmeldinger.dat", ios::out | ios::in | ios::binary);
fil.seekp((ID - 1) * sizeof(enfeil));
fil.write(reinterpret_cast <const char*>(&enfeil),
sizeof(Feil));
fil.close();
}
void skrivUtFeil(){
int idnr = 1;
//for(int i = 0; i < 2; i++){
Feil enfeil;
int antallFeil;
ifstream innFil;
innFil.open("FeilTeller.dat");
innFil >> antallFeil;
innFil.close();
innFil.open("Feilmeldinger.dat", ios::in | ios::binary);
innFil.read(reinterpret_cast <char*>(&enfeil),
sizeof(Feil));
innFil.clear();
//Prints out Feil with ID "idnr"
innFil.seekg((idnr - 1) * sizeof(enfeil));
innFil.read(reinterpret_cast <char*>(&enfeil),
sizeof(Feil));
cout << enfeil << endl;
cout << "Totalt antall feil: " << antallFeil << endl;
system("pause");
innFil.close();
//}
} //Exception on this line
Your class Feil contains string dato, so it's not trivially copyable. Therefore it's illegal to write and read it as raw binary data.
What happens if you do it? std::string contains a pointer to character array, so you write its value, not content. When you read it, you read the same value into the pointer, but it's pointing to garbage.

How to parse content of a file and load into the map

i want to parse the content of a file and load into a map.
This is the file content format:
Movie-name release-year price cast ishd
"DDLG" 2010 20.00 "shahrukh and Kajal" true
"Aamir" 2008 10.00 "abc, xyz and ijkl" false
Key for map will be the first word (Movie name).
class defition:
class movieInfo
{
private:
int releaseYear;
double price;
string cast;
bool isHD;
};
This is the function i am trying to implement.
void fill_map_contents (map <string, movieInfo*> &mymap, ifstream& myfile)
{
string line;
string word;
while (getline(myfile, line))
{
out << "inside while loop " << line << endl;
stringstream tokenizer;
tokenizer << line;
movieInfo *temp = new movieInfo;
while (tokenizer >> word)
{
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
mymap[word] = temp;
}
delete temp;
}
}
I am not getting any idea, after while (tokenizer >> word), how to fill object variable and assign it to map.
Any help will be highly appreciated.
Devesh
cout << " printing word :->"<< word << endl;
//temp->releaseYear = atoi(word);
//temp->price = 12.34;
//temp->cast = "sahahrukh salman";
//temp->isHD = false;
in above code you are trying to access private members of the class directly which is not possible.Hence ,better solution is you should include public getter/setter for each variable as follows.
public:
void setreleaseYear(int sry){releaseYear=sry;}
void setprice(double pr){price=pr;}
void setcast(string cast){string=str;}
void setisHD(bool id){isHD=id;}
now use in place of commented code :
//temp->releaseYear = atoi(word);
temp->setreleaseYear(atoi(word));
tokenizer >> word;
//temp->price = 12.34;
temp->setprice(atof(word));
tokenizer >> word;
//temp->cast = "sahahrukh salman";
temp->setcast(word);
tokenizer >> word;
//temp->isHD = false;
temp->setisHD(word);
No need of while loop.
You must simplify things. I suggest to add inserction and extraction operator for movieinfo and choose new line as field separator
DDLG
2010
20.00
shahrukh and Kajal
true
Aamir
2008
10.00
abc, xyz and ijkl
false
class movieInfo
{
public:
int releaseYear;
double price;
string cast;
bool isHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear << '\n'
<< i.price << '\n'
<< i.cast << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.releaseYear
>> i.price;
getline( is, i.cast );
return is >> std::boolalpha >> i.isHD;
}
};
void fill_map_contents (map <string, movieInfo> &mymap, ifstream& myfile)
{
while ( !myfile.eof )
{
string name;
myfile >> name;
movieInfo mi;
myfile >> m1;
mymap[ name ] = movieInfo;
}
}
note that I changed map <string, movieInfo*> in map <string, movieInfo> prefer using move semantic.
Another suggestion I will change moveinfo in:
class movieInfo
{
public:
// ctor and move, assign operator and move operator
int releaseYear() const { return mReleaseYear; };
double price() const { return mPrice; };
const string& cast() const { return mCast; };
bool isHD() const { return mIsHD; };
private:
int mReleaseYear;
double mPrice;
string mCast;
bool mIsHD;
friend std::ostream& operator << ( std::ostream& os, const movieinfo& i )
{
return os << i.releaseYear() << '\n'
<< i.price() << '\n'
<< i.cast() << '\n'
<< std::boolalpha << i.isHD() << '\n';
}
friend std::istream& operator >> ( std::istream& is, movieinfo& i )
{
is >> i.mReleaseYear
>> i.mPrice;
getline( is, i.mCast );
return is >> i.mIsHD;
}
};
You are effectively trying to parse a CSV file, with space as separator and " as quote character.
I'd recommend using a library for this, like this one.
Example code (taken from the help page):
// Note: I changed mymap to map<string, movieInfo> without a pointer - it's not
// needed
const char field_terminator = ' '; // Use a space
const char line_terminator = '\n'; // Use line break
const char enclosure_char = '"'; // Use "
csv_parser file_parser;
file_parser.set_skip_lines(1);
file_parser.init(filename);
file_parser.set_enclosed_char(enclosure_char, ENCLOSURE_OPTIONAL);
file_parser.set_field_term_char(field_terminator);
file_parser.set_line_term_char(line_terminator);
while(file_parser.has_more_rows()) {
csv_row row = file_parser.get_row();
movieInfo temp; // No need for pointers
temp->releaseYear = atoi(row[1]); // C++11: Use std::stoi()
temp->price = atof(row[2]); // C++11: Use std::stof()
temp->cast = row[3];
temp->isHD = row[4].compare("true") == 0;
mymap[row[0]] = temp;
}