Question

Hi I'm trying to fill in an array of a class object I created. Input is from a text file. The text file has strings and numbers. I got the first set of info in but the rest of the file won't read in, any thoughts would be appreciated!

    class mess
    {
        private: 
            string name;
            float age, weight, height;
        public: 
            void setname(string a) {name=a;}
            void setage(float b){age=b;}
            //etc.
            string getname(){return name;}
            float getage(){return age;}
            //etc.
    }

    ifstream input;
    input.open("test.txt");

    mess people[2]
    string str;
    float num;

    for(inti=0; i<2; i++)
    {
        getline(input,str,'\n');
        people[i].setname(str);

        input >> num;
        people[i].setage(num);

        input >> num;
        people[i].setweight(num);

        input >> num;
        people[i].setheight(num);
    }
    for(inti=0; i<2; i++)
    {
        cout << people[i].getname() << endl;
        cout << people[i].getage() << endl;
        cout << people[i].getweight() << endl;
        cout << people[i].getheight() << endl;
    }

test.txt

jack
17    150.3    5.10
Amy
18    110.4    5.11

Output:

Jack
17
150.3
5.10
(blank)
0
0
0
Was it helpful?

Solution

The problem here is that when you use the input operator >> it will leave the newline after the last number for the first record. This means that the next getline call will read that newline as an empty line, and then the numbers will fail to read.

There are a couple of ways to solve this. The first is to discard all text in the input until newline after reading the last number in the record. For this you can do e.g.

// All other input in loop

input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Read about the ignore function.

Another way is to read the second line, complete, and put it into an std::istringstream and then read out the numbers from it:

// Reading the name...

std::string numbers;
std::getline(input, numbers);

std::istringstream istr(numbers);

istr >> num;
people[i].setage(num);

// ...

Also note that the third argument to std::getline already defaults to a newline, so if you're using it to read lines, then you don't need to provide it.

OTHER TIPS

I suggest you overload operators << and >> in your class:

class mess
{
    private: 
        string name;
        float age, weight, height;
    public: 
        void setname(string a) {name=a;}
        void setage(float b){age=b;}
        //etc.
        string getname(){return name;}
        float getage(){return age;}
        //etc.
        friend std::istream& operator>>(std::istream& inp, mess& m);
        friend std::ostream& operator<<(std::ostream& out, const mess& m);
}

std::istream& operator>>(std::istream& inp, mess& m)
{
    std::getline(inp, m.name);
    inp >> m.age;
    inp >> m.weight;
    inp >> m.height;
    return inp;
}

std::ostream& operator<<(std::ostream& out, const mess& m)
{
    out << m.name   << endl;
    out << m.age    << endl;
    out << m.weight << endl;
    out << m.height << endl;
    return out;
}

This simplifies your input to:

std::vector<mess> people;
mess              p;
while (input_file >> p)
{
  people.push_back(p);
}

Your output looks like:

for (unsigned int i = 0; i < people.size(); ++i)
{
  cout << people[i];
  cout << "\n";
}

I would define the operator<< to write out an object of your class. Then defined the operator>> to read an object of your class. Then you can use std::istream_iterator to read the values directly into the container:

class M
{
    private:
        string name;
        float  age;
        float  weight;
        float  height;
    public:
        <STUFF>

    friend std::ostream& operator<<(std::ostream& s, M const& data)
    { 
        return s << data.age    << " " 
                 << data.weight << " " 
                 << data.height << " " 
                 // Put name on the edn because it may contain space.
                 // So we want to read it off using std::getline
                 << data.name   << "\n";
    }
    friend std::istream& operator>>(std::istream& s, M& data)
    { 
        s >> data.age >> data.wight >> data.height;
        std::getline(s, data.name);

        // Strip leading space (see output operator)
        data.name = data.name.substring(1);

        return s;
    }

};

Then easy to use in most containers.

int main()
{
    std::ifstream  f("data");
    // OK.
    // A vector is not actually an array.
    // But you can use the same principle with a manual loop.
    // as std::istream_iterator is just a normal iterator.
    std::vector<M> data(std::istream_iterator<M>(f), std::istream_iterator<M>());
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top