How to pass this string C++ - c++

So I got this assignment and I have everything working perfectly except calling this one function that is supposed to format the email string. If not calling this function, I can print it exactly the way the professor says it is supposed to be. However, with calling this function, I can't seem to get it to return the information and print it. Can anyone help me out with this? I have spent an unfortunate amount of hours trying to get this one thing worked out.
//Function Prototypes
std::string FormatEmailString(std::string from, std::string to, std::string subject, std::string msg);
std::string GetInboxFile(std::string username);
std::string GetLine(std::istream & sin);
int main()
{
std::string email;
std::string from;
std::string to;
std::string subject;
std::string msg;
std::ifstream fin;
std::ofstream fout;
std::string user1;
fin.open(user1 + "-inbox.txt", std::ios::in);
while (fin.eof() == false) // I know fin.eof is not a good method to use.
{ // I tried while(fin) but it lets extra "From:" "To"...
email = GetLine(fin); //GetLine is a function call to getline.
from = GetLine(fin);
to = GetLine(fin);
subject = GetLine(fin);
msg = GetLine(fin);
std::string formatted = FormatEmailString(from, to, subject, msg);
fout << formatted;
}
std::cout << std::endl;
fin.close();
std::cin.get();
return 0;
}
std::string FormatEmailString(std::string from, std::string to, std::string subject, std::string msg)
{
std::ostringstream out;
std::ifstream fin;
std::string user1;
fin.open(user1 + "-inbox.txt", std::ios::in);
out << std::endl;
out << "From: " << from << std::endl;
out << "To: " << to << std::endl;
out << "Subject: " << subject << std::endl;
out << "Message: " << msg << std::endl;
fin.close();
return out.str();
}//END FormatEmailString
std::string GetLine(std::istream & sin)
{
std::string s;
std::getline(sin, s);
return s;
}//END GetLine
//FROM user1-inbox.txt TEXT FILE:
#email //This Line not printed
abc //FROM
user1 //TO
hello //Subject
How about lunch? //Message
#email //This line not printed
abc //From
user1 //To
Join the Dark Side //Subject
We have cookies! //Message
//END TEXT FILE

you are not opening file which you want to write into.
your fout is used only in two places... in declaration and then u are writing into it.

Related

Writing and reading objects to and from file

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.

Reading or writing binary file incorrectly

The output of the code show gibberish values for all the variables of the Student struct. When the display function is ran.
I've include the relevant code in each of the add and display function for the binary file.
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
//Student struct
struct Student
{
char name [30];
float labTest;
float assignments;
float exam;
};
//Writing function
afile.open(fileName,ios::out|ios::binary);
Student S;
strcpy(S.name,"test");
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
//Reading function
afile.open(fileName,ios::in|ios::binary);
afile.seekg(0,ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
cout << "Name of Student: " << S.name << endl
<< "Lab mark: " << S.labTest << endl
<< "Assignment mark: " << S.assignments << endl
<< "Exam mark: " << S.exam << endl << endl;
}
afile.close();
There are a lot of problems with your code:
Calling your write function will permanently overwrite the last written data set. You have to add: ios::append, so that new data will be written behind the last data you wrote before.
After you move with afile.seekg(0,ios::end); to get with tellg the file size, you have to go back to the start of the file before reading with afile.seekg(0,ios::beg)
It looks that you use a char array to store a string. This is not c++ style! And it is dangerous how you use it. If you use strcpy, you can copy a string which is longer than the space you reserved for it. So you should prefer std::string for that. But you can't simply write a struct which constains std::string as binary! To get checked copy you can use strncpy, but that is still not c++ ;)
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
Yes, the file position moves which each successful read and write.
A general remark writing binary data by simply dumping memory content:
That is not a good idea, because you can only read that data back, if you use the same machine type and the same compiler options. That means: A machine with different endianness will read data totally corrupted. Also a different integer type ( 32 bit vs 64 bit ) will break that code!
So you should invest some time how to serialize data in a portable way. There are a lot of libraries around which can be used to read/write also complex data types like std::string or container types.
A hint using SO:
Please provide code which everybody can simply cut and paste and compiled. I did not know what your Student struct is. So I take a lot of assumptions! Is your struct really using char[]? We don't know!
#include <iostream>
#include <fstream>
#include <cstring>
const char* fileName="x.bin";
struct Student
{
char name[100]; // not c++ style!
int labTest;
int assignments;
int exam;
};
// Writing function
void Write()
{
std::ofstream afile;
afile.open(fileName,std::ios::out|std::ios::binary|std::ios::app);
Student S;
strcpy(S.name,"test"); // should not be done this way!
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
}
void Read()
{
//Reading function
std::ifstream afile;
afile.open(fileName,std::ios::in|std::ios::binary);
afile.seekg(0,std::ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
afile.seekg(0, std::ios::beg);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
std::cout << "Name of Student: " << S.name << std::endl
<< "Lab mark: " << S.labTest << std::endl
<< "Assignment mark: " << S.assignments << std::endl
<< "Exam mark: " << S.exam << std::endl << std::endl;
}
afile.close();
}
int main()
{
for ( int ii= 0; ii<10; ii++) Write();
Read();
}
EDIT. Apparently, I was a bit too late in responding. Klaus has compiled a better, more comprehensive response dwelling into other problems regarding C-style char [], std::string and the endianness of the platform.
You should append to the file opened for every record. In your code you don't have this, at all. Please write the code in a way we can copy and paste, and test. As a working example, you should write some code that can be compiled and run as below:
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// Student struct
struct Student {
char name[30];
float labTest;
float assignments;
float exam;
};
// Serializer
void serialize_student(const Student &s, const std::string &filename) {
// Append to the file, do not overwrite it
std::ofstream outfile(filename, std::ios::binary | std::ios::app);
if (outfile)
outfile.write(reinterpret_cast<const char *>(&s), sizeof(Student));
}
// Deserializer
std::vector<Student> deserialize_students(const std::string &filename) {
std::ifstream infile(filename, std::ios::binary);
std::vector<Student> students;
Student s;
while (infile.read(reinterpret_cast<char *>(&s), sizeof(Student)))
students.push_back(std::move(s));
return std::move(students);
}
int main(int argc, char *argv[]) {
// Generate records
std::vector<Student> mystudents;
std::generate_n(std::back_inserter(mystudents), 10, []() {
Student s;
std::strcpy(s.name, "test");
s.labTest = rand() % 100 + 1;
s.assignments = rand() % 100 + 1;
s.exam = rand() % 100 + 1;
return s;
});
// Print and write the records
for (const auto &student : mystudents) {
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
serialize_student(student, "students.bin");
}
// Read and print the records
auto records = deserialize_students("students.bin");
std::cout << "===\n";
for (const auto &student : records)
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
return 0;
}

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.

Set std:cin to a string

For ease of testing I wish to set Cin's input to a string I can hardcode.
For example,
std::cin("test1 \ntest2 \n");
std::string str1;
std::string str2;
getline(cin,str1);
getline(cin,str2);
std::cout << str1 << " -> " << str2 << endl;
Will read out:
test1 -> test2
The best solution IMO is to refactor your core code to a function that accepts a std::istream reference:
void work_with_input(std::istream& is) {
std::string str1;
std::string str2;
getline(is,str1);
getline(is,str2);
std::cout << str1 << " -> " << str2 << endl;
}
And call for testing like:
std::istringstream iss("test1 \ntest2 \n");
work_with_input(iss);
and for production like:
work_with_input(cin);
While I agree with #πάντα ῥεῖ that the right way to do this is by putting the code into a function and passing a parameter to it, it is also possible to do what you're asking for, using rdbuf(), something like this:
#include <iostream>
#include <sstream>
int main() {
std::istringstream in("test1 \ntest2 \n");
// the "trick": tell `cin` to use `in`'s buffer:
std::cin.rdbuf(in.rdbuf());
// Now read from there:
std::string str1;
std::string str2;
std::getline(std::cin, str1);
std::getline(std::cin, str2);
std::cout << str1 << " -> " << str2 << "\n";
}

Infinite loop while using pointers

I am having problem when trying to do pointer loop in C++. What I am trying to do is, user can keep enter message and the message on the new line will be appended. It will only stop prompting when "." is detected at the beginning of a new line. Here is my main method:
vector <Message*> message_list;
Message* message1 = new Message("Student1", "Student2");
cout << "Enter message text line, enter . on new line to finish: " << endl;
while(getline(cin, message1->get_text_input()))
{
if(message1->get_text_input() == ("."))
{
break;
}
else
{
message1->append(message1->get_text_input());
}
}
}
And this is my .cpp file:
Message::Message(string recipient, string sender)
{
this->recipient = recipient;
this->sender = sender;
}
string Message::get_text_input()
{
return text_input;
}
void Message::append(string text)
{
message += text + "\n";
}
string Message::to_string() const
{
return ("From: " + sender + "\n" + "To: " + recipient + "\n");
}
void Message::print() const
{
cout << message;
}
My header class:
class Message
{
public:
Message(std::string recipient, std::string sender);
std::string get_text_input();
void append(std::string text);
std::string to_string() const;
void print() const;
private:
std::string recipient;
std::string sender;
std::string message;
std::string text_input;
char* timestamp;
};
Does anybody know why is it so? Even ".' is detected, it still wont stop.
Thanks in advance.
In the getline(cin, message1->get_text_input()) you get the field text_input returned by value. So now you have a brand new string, in which you fill the message, but it is instantly destroyed, because it never gets a name.
To solve your Problem, make get_text_input return a reference:
string& Message::get_text_input()
{
return text_input;
}
That way, the input string will get the line into the original string.
Also, go and look up References and Values, you will need them a lot.