p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/index.php)
-   BOOK: Professional C++, 2nd Edition (http://p2p.wrox.com/forumdisplay.php?f=661)
-   -   Chapter 15 Demystifying C++ I/O (http://p2p.wrox.com/showthread.php?t=98868)

phztfte1 May 14th, 2016 10:49 PM

Chapter 15 Demystifying C++ I/O
 
Marc,

Storing a class object in a container is temporary, and all data is lost when the program terminates. How do you store a class object data in a file and read that data afterwards? Say, the data members are two strings (e.g., mFirstName and mLastName) and a character (e.g. mMiddleInitial).

Marc Gregoire May 15th, 2016 07:01 AM

On page 559, there is a section called "Input and Output with Objects" that explains how to add support for operator<< and >> to your own objects.
With these you can then stream your objects to for example a file stream discussed elsewhere in the same chapter.

phztfte1 May 15th, 2016 03:43 PM

Chapter 15 Demystifying C++ I/O
 
Sorry Marc, but I was not able to put the pieces together. I need to see an explicit example.
I modified the void output() method to write to a file. It is shown in the code as boldfaced.
Now, how do you read the Muffin.txt file?
Code:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

using namespace std;

class Muffin
{
    public:
        string    getDescription() const;
        void      setDescription(const string& inDesc);
        int      getSize() const;
        void      setSize(int inSize);
        bool      getHasChocolateChips() const;
        void      setHasChocolateChips(bool inChips);

        void      output();
    protected:
        string    mDesc;
        int      mSize;
        bool      mHasChocolateChips;
};

string Muffin::getDescription() const { return mDesc; }
void Muffin::setDescription(const string& inDesc) { mDesc = inDesc; }
int Muffin::getSize() const { return mSize; }
void Muffin::setSize(int inSize) { mSize = inSize; }
bool Muffin::getHasChocolateChips() const { return mHasChocolateChips; }
void Muffin::setHasChocolateChips(bool inChips) { mHasChocolateChips = inChips; }

void Muffin::output()
{
        ofstream outFile("Muffin.txt", ios::out | ios::app);
        if(!outFile.is_open()) {
            cerr << "File could not open!";
            return;
        }
        outFile << getDescription().c_str() << getSize() << getHasChocolateChips() << '\n';
        outFile.clear();
        outFile.close();
}

Muffin createMuffin(istringstream& inStream);

int main()
{
  cout << "First, let's create a muffin in code and output it." << endl;

  Muffin m;
  m.setDescription("Giant_Blueberry_Muffin");
  m.setSize(42);
  m.setHasChocolateChips(false);

  m.output();

  cout << "Now we'll create a muffin from a string stream" << endl;

        istringstream instream("My_Muffin 2 true");
  Muffin m2 = createMuffin(instream);

  m2.output();

  return 0;
}

Muffin createMuffin(istringstream& inStream)
{
  Muffin muffin;
  // Assume data is properly formatted:
  // Description size chips

  string description;
  int size;
  bool hasChips;

  // Read all three values. Note that chips is represented
  // by the strings "true" and "false"
  inStream >> description >> size >> boolalpha >> hasChips;
  muffin.setSize(size);
  muffin.setDescription(description);
  muffin.setHasChocolateChips(hasChips);

  return muffin;
}


The output in the Muffin.txt file was as follows:
Code:


Giant_Blueberry_Muffin420
My_Muffin21


Marc Gregoire May 16th, 2016 04:08 AM

You'll have to format the data in such a way that you can read it back.
If you look at the createMuffin() method, you see that it is able to parse/read muffins formatted as follows:
Muffin_Name Size Chips
So, name followed by a space, followed by the size, a space and true or false for the chocolade chips.
If you want to use createMuffin to read data from a file, then you should modify output() to write it in that format. So, for example:
Code:

void Muffin::output()
{
        ofstream outFile("Muffin.txt", ios::out | ios::app);
        if(!outFile.is_open()) {
            cerr << "File could not open!";
            return;
        }
        outFile << getDescription() << " " << getSize() << " " << boolalpha << getHasChocolateChips() << '\n';
}

Some notes:
* No need for the clear() or close(). The file is automatically closed.
* booalpha is used to write true/false instead of 1/0
* no need for the .c_str() when writing a std::string to a C++ stream.
* This obviously only works if there are no spaces in the name of the muffin. If you want to support spaces in the name, then you can take a look at the quoted manipulator. (see https://msdn.microsoft.com/en-us/library/dn986842.aspx)

phztfte1 May 18th, 2016 11:30 PM

Chapter 15 Demystifying C++ I/O
 
Hi Marc,

I revised the output() method as you suggested and added an input() method. The input() method does not work but at least, it is a start.
1. The input() method does not read the bool value for chips correctly. Instead of reading false, it shows true.
2. The exercise raises a question: how do you get to read the next record?

Code:

include <iostream>
#include <sstream>
#include <string>
#include <fstream>

using namespace std;

class Muffin
{
    public:
        string    getDescription() const;
        void      setDescription(const string& inDesc);
       
                int      getSize() const;
        void      setSize(int inSize);
       
                bool      getHasChocolateChips() const;
        void      setHasChocolateChips(bool inChips);

        void      output();
                void          input();
    protected:
        string    mDesc;
        int      mSize;
        bool      mHasChocolateChips;
};

string Muffin::getDescription() const { return mDesc; }
void Muffin::setDescription(const string& inDesc) { mDesc = inDesc; }
int Muffin::getSize() const { return mSize; }
void Muffin::setSize(int inSize) { mSize = inSize; }
bool Muffin::getHasChocolateChips() const { return mHasChocolateChips; }
void Muffin::setHasChocolateChips(bool inChips) { mHasChocolateChips = inChips; }

void Muffin::output()
{
  printf("%s, Size is %d, %s\n", getDescription().c_str(), getSize(),   
    (getHasChocolateChips() ? "has chips" : "no chips"));

  ofstream outFile("Muffin.txt", ios::out | ios::app);
  if (!outFile) {
          cerr << "File could not open!" << endl;
          return;
  }
  outFile << getDescription() << '^' << getSize() << '^' << boolalpha << getHasChocolateChips() << '\n';
}


void Muffin::input()
{
        string description;
        int size;
        bool chips;

        ifstream inFile("Muffin.txt", ios::in);
        if (!inFile) {
                cerr << "File could not open!" << endl;
                return;
        }
                getline(inFile, description, '^');
                inFile >> size;
                inFile >> chips;
                cout << "inFile: " << description << " " << size << " " << boolalpha << chips << endl;
}


Muffin createMuffin(istringstream& inStream);

int main()
{
  cout << "First, let's create a muffin in code and output it." << endl;

  Muffin m;
  m.setDescription("Giant Blueberry Muffin");
  m.setSize(42);
  m.setHasChocolateChips(false);
  m.output();

  cout << "Now we'll create a muffin from a string stream" << endl;
  istringstream instream("My_Muffin 2 true");
  Muffin m2 = createMuffin(instream);
  m2.output();

  Muffin muff;
  muff.input();

  return 0;
}

Muffin createMuffin(istringstream& inStream)
{
  Muffin muffin;
  // Assume data is properly formatted:
  // Description size chips

  string description;
  int size;
  bool hasChips;

  // Read all three values. Note that chips is represented
  // by the strings "true" and "false"
  inStream >> description >> size >> boolalpha >> hasChips;
  muffin.setSize(size);
  muffin.setDescription(description);
  muffin.setHasChocolateChips(hasChips);

  return muffin;
}

Here are the contents of the text file.
Code:


Giant Blueberry Muffin^42^false
My_Muffin^2^true


Marc Gregoire May 19th, 2016 02:00 PM

One option is to write them as follows:
Code:

outFile << quoted(getDescription()) << ' ' << getSize() << ' ' << boolalpha << getHasChocolateChips() << '\n';
Note that I'm using the quoted() manipulator and replaced the ^ with spaces.
The following is then one way to read the data back:
Code:

        while (!inFile.eof())
        {
                description = "";
                inFile >> quoted(description);
                if (description.empty())
                {
                        break;
                }
                inFile >> size >> boolalpha >> chips;
                cout << "inFile: " << description << " " << size << " " << boolalpha << chips << endl;
        }


phztfte1 May 19th, 2016 10:20 PM

Chapter 15 Demystifying C++ I/O
 
Marc,

I did not use quoted because when I read the documentation it was for Visual Studio 2015 and new to C++14. There is a red squiggly line under quoted even though the header file <iomanip> was added. I use Visual Studio 2013 Professional which does not implement C++14.

So, how does one do it without quoted?

Marc Gregoire June 1st, 2016 01:40 PM

Just try to remove the quoted() then.
In that case of course, the name cannot contain spaces.
If you do want to support spaces, then you'll have to do some more parsing yourself, for example by quoting it yourself.

phztfte1 June 3rd, 2016 09:58 AM

Chapter 15 Demystifying C++ I/O
 
Say my data members were an int and a double. When writing to a text file, are ithe int and double converted to text?

Marc Gregoire June 4th, 2016 09:35 AM

If you write them with operator <<, then yes, they are converted to text.


All times are GMT -4. The time now is 08:58 PM.

Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.