Analogous network protocol for transmitting messages using istream/ostream - c++

This was a question from a coding challenge and I couldn't figure out the way to do it -
Implement encode() and decode() for a simple wire protocol per the prototypes below:
void encode ( const std::string& inputFilename, std::ostream& out );
void decode ( std::istream& in, const std:string& outputFilename );
Lets say there are several files and each contains a single message. As it already says from the prototype, encode must read single specified file from disk and place message in the ostream.
Decode must read single encoded message from istream and place it in the specified file. The contents of the two corresponding files must be identical.
It is easy if I could read the file in the encode method and place the data in a buffer and send them to another file in the decode method, but that is not the question. How do I read data from std::istream without someone actually typing the data on the console (like std::cin)?
Thank you in advance for your time and would love to see responses for this!

istream is a parent class for ifstream (input file stream) and istringstream (input string stream), so you can pass as a input parameter ifstream or istringstream. Do not need to use cin.
The same applies to out parameter, you can provide object of the ofstream or ostringstream. This is well know mechanism in C++ to abstract what particular type of stream you deal with.

I wrote a piece of code that works with the prototypes given in the question. I am sure there could be more ways of doing it but I thought I would share this.
class fileIO
{
private:
std::string inBuf;
public:
std::stringstream testbuf;
void encode(const std::string& inputFilename, std::ostream& out);
void decode ( std::istream& in, const std::string& outputFilename);
};
void fileIO::encode(const std::string& inputFilename, std::ostream& out)
{
//read from the input file and stream it to ostream
ifstream infile(inputFilename);
while(infile.good()){
getline(infile, inBuf);
out << inBuf;
}
cout << "Value stored in outBuf: " << inBuf << endl;
infile.close();
}
void fileIO::decode(std::istream& in, const std::string& outputFilename)
{
//read from istream and output it to a file
string val;
ofstream ofile(outputFilename);
in >> val;
cout<< "Read the istream contents: " << val << endl;
ofile << val;
ofile.close();
}
int main( int argc, char ** argv )
{
fileIO File;
string inputFile;
cout << "Enter input File Name: "<< endl;
cin >> inputFile;
File.encode(inputFile, File.testbuf);
File.decode(File.testbuf, inputFile);
return 0;
}
I am passing "a stringstream" to both encode and decode since istream and ostream are the parent class of stringstream. In this solution, the value from the input file is stored in the stringstream and gets passed on to decode where the value from the same stringstream gets written to output file.

Related

I'm trying to write a char into a .txt file by using the ifstream getline function. But i get an Error Message

E0304 no instance of overloaded function "std::basic_ifstream<_Elem, _Traits>::getline [with _Elem=char, _Traits=std::char_traits]" matches the argument list
Im using a struct for the Information:
struct customer {
int id;
char name;
char phone;
char address;
};
And im trying to write the Customers Information into a .txt file:
void customerData()
{
ifstream ifs;
ifs.open("Customer.txt");
int custNum = 0;
while (!ifs.eof())
{
ifs >> cust[custNum].id;
ifs.ignore();
ifs.getline(cust[custNum].name, 100, ';');
ifs.getline(cust[custNum].phone, 15, ';');
ifs.getline(cust[custNum].id, 15, ';');
ifs.getline(cust[custNum].address, 1500);
custNum++;
}
}
I cant figure out how to fix the above posted Error on the getline functions.
There are big mistakes in your code that guys pointed out.
You are not writing to the file, you are reading it.
You cannot store a full name in a single character.
Actually, if you want to store this data, you should use character array or std::string.
So your struct will be like this :
struct customer {
int ID;
char name[100];
char phone_number[15];
char address[1500];
/*
OR
int ID;
std::string name;
std::string phone_number;
std::string address;
in this case it's better to use std::string instead of using 1500 characters for address
*/
}
Also, getline is not for writing to the file (as you said you want to write in file) , it is used for reading from the file.
So your customerData function will look like this:
// saving in file
ofstream ofs(Customer.txt);
// check if file is created
if(ofs.is_open(){
ofs << name << '\n';
ofs << address << '\n';
ofs << phone_number << '\n';
ofs << id << '\n';
// This is a simple way to store data in a file.
// There are other ways to store data in a file..
// I used this because you can use getline to read them and get the data as lines.
}

I'm getting Read access violation while accessing objects from file C++.

In the below class error is in the init function where i load the class object I stored in the file to the vector Items.
class Item
{
std::string item_code;
std::string item_name;
std::string unit_name;
unsigned int price_per_unit;
double discount_rate;
static std::vector<Item> Items;
friend std::ostream& operator<< (std::ostream&, Item&);
public:
static void PrintAll();
static void Init();
~Item();
};
Default constructor is the one which reads data from user and writes into file. Below is the code of default constructor.
Item::Item(int a)
{
std::cout << "Item name : ";
std::getline(std::cin, item_name);
std::cout << "Unit (Kg/g/Qty) : ";
std::getline(std::cin, unit_name);
std::cout << "Price per unit : ";
std::cin >> price_per_unit;
std::cout << "Discount Rate : ";
std::cin >> discount_rate;
std::cin.ignore();
std::cout << "Product code (has to be unique) : ";
std::getline(std::cin, item_code);
std::ofstream outfile;
outfile.open("Files\\Items.txt", std::ios::out | std::ios::app);
outfile.write((char*)&(*this), sizeof(Item));
outfile.close();
}
Below is the Init() function for which read access violation is thrown at.
void Item::Init()
{
std::ifstream infile("Files\\Items.txt", std::ios::in);
if (!infile.is_open())
{
std::cout << "Cannot Open File \n";
infile.close();
return;
}
else
{
Item temp;
while (!infile.eof())
{
infile.read((char*)&temp, sizeof(temp));
Item::Items.push_back(temp);
}
}
infile.close();
}
Even though i am checking for eof, read access violation is thrown. Please give me some advice on this issue.
infile.read((char*)&temp, sizeof(temp));
This fills the temp object with junk from the file. It's supposed to contain valid std::string objects and whatever is in the file, it can't possibly be a valid std::string object. If you don't see why, consider that creating a valid std::string object requires allocating memory to hold the string data -- that's what the std::string constructor does. Reading data from a file can't possibly do this.
A file is a stream of bytes. To write data to a file, you need to define some way to represent that data as a stream of bytes. You need to encode its length if it is variable length. To read it back in, you need to handle the variable length case as well. You need to convert the file data to an appropriate internal representation, such as std::string. This is called "serialization".
std::string size is variable, you can try the following definition
char item_code[20];
char item_name[20];
char unit_name[20];

switching between cin and ifstream using a function

I am trying to manipulate an array with functions while switching between standard cin, cout and ifstream,ostream.
Specifically, I have an array of books and I have some basic functions like search title, publisher, price, etc. I also have 2 functions called "login" and "logout" to open a file and redirect bookList's istream and ostream to that outputfile when login, as well as close it and return back to istream, ostream when logout.
void bookList(istream& in, ostream& out)
{
//ask for command from istream in
//command selection loop
}
int load(ofstream& out, book booklist[], int size)
{
//load list of books from input file
}
void logon(ofstream& out, string filename)
{
out.open(filename.c_str());
}
void logoff(ofstream& out, string filename)
{
out.close();
}
// some other functions
I also need to print out notification to the user (either onscreen when logged off or on file when logged on) whenever a function is called.
My tried to put ifstream& as a parameter in each functions, but they only print out to text file not on screen (because its just ifstream, not istream), but doing it the other way won't work.
My question is that is there method that can make function logon redirect istream of bookList to ifstream to the outputfile and vice versa for logoff? Rather than a "is-file-open" condition.
This may not be directly what your looking for but can be modified.
/// this buffer will be used to switch output
void IO_Switch(streambuf* buffer);
int main(){
streambuf *buffer
ofstream fout;
fout.open("filename.txt");
// below you call cout.rdbuf() which directs stream to cout
IO_Switch(cout.rdbuf());
cout << "This is coming to the console";
// below you call fout.rdbuf());
IO_Switch(fout.rdbuf());
cout << "I used cout here, but the stream is redirected to fout ("filename.txt")
return 0;
}
void IO_Switch(streambuf* buffer){
cout.rdbuf(buffer);
}

Read in from file into structure

I want to read in from txt file into structure using fstream.
I save the data to the file in the way shown below:
To read the data i tried some cheeky stuff with getlines or tabsin<
struct tab{
int type,use;
string name, brand;
};
tab tabs[500];
ofstream tabsout;
tabsout.open("tab.txt", ios::out);
for (int i = 0; i < 500; i++){
if (tabs[i].use==1){
tabsout << tabs[i].type << " " << tabs[i].name << " " << tabs[i].brand << "\n";
}
}
tabsout.close();
//input part that fails me :(
int i=0;
ifstream tabsin;
tabsin.open("tab.txt", ios::in);
if (tabsin.is_open()){
while(tabsin.eof() == false)
{
tabsin >> tabs[i].type>>tabs[i].name>>tabs[i].brand;
i++
}
tabsin.close();
You usually want to overload operator>> and operator<< for the class/struct, and put the reading/writing code there:
struct tab{
int type,use;
string name, brand;
friend std::istream &operator>>(std::istream &is, tab &t) {
return is >> t.type >> t.name >> t.brand;
}
friend std::ostream &operator<<(std::ostream &os, tab const &t) {
return os << t.type << " " << t.name << " " << t.brand;
}
};
Then you can read in a file of objects like:
std::ifstream tabsin("tab.txt");
std::vector<tab> tabs{std::istream_iterator<tab>(tabsin),
std::istream_iterator<tab>()};
....and write out the objects like:
for (auto const &t : tabs)
tabsout << t << "\n";
Note that (like any sane C++ programmer) I've used a vector instead of an array, to (among other things) allow storing an arbitrary number of items, and automatically track how many are actually being stored.
For starters, do not use .eof() to control your loop: it doesn't work. Instead, use the stream's state after reading:
int type;
std::string name, brand;
while (in >> type >> name >> brand) {
tabs.push_back(tab(type, name, brand));
}
If your name or brand contain spaces, the above won't work and you will need to write a format where you can know when to stop abd read correspondingly, e.g., using std::getline().
You might also consider wrapping the logic to read or write an object by suitable operators.
istream& getline (istream& is, string& str, char delim);
Take a look at the third parameter, you can use std::getline to parse your line. But that is definitely not the best way to serialize objects. Instead of using a text file, you should use a byte stream.

How do I pass a ostream and use cout to display on the screen in a function?

I want to use istream and ostream as a parameter that my functions take and help me to the work (read file, and display content on the screen).
I know how to do it with istream:
std::ifstream myfile(...);
void foo(myfile);
void readFoo(std::istream &stream)
{
int x, y;
stream >> x >> y; //suppose my file contains many rows of 2 numbers.
//store the x and y to somewhere
}
void writeFoo(std::ostream &output)
{
???
}
And what object should I pass to my writeFoo()?
UPDATE:
Here is the update but I got an error message( cannot convert from ostream to ostream*)
writeFoo(std::cout);
writeFoo(sd::ostream &out)
{
out << somedata to display to the screen;
}
You can pass any output stream which derives from std::ostream.
For example:
writeFoo(std::cout); //write to stdout
std::ofstream file("output.txt"); //open/create a file first
writeFoo(file); //write to output.txt
Hope that helps.