The biggest potential problems you are running up against is the fact that you assume a line is valid if it begins with a c
, t
or r
without first validating the remainder of the line matches the format for a circle, triangle or rectangle. While not fatal to this data set, what happens if one of the lines was 'cat'
or 'turtle'
?
By failing to validate all parts of the line fit the "mold" so to speak, you risk attempting to output values of r
, h & w
or s
that were not read from the file. A simple conditional check of the read to catch the potential failbit
or badbit
will let you validate you have read what you think you read.
The remainder is basically semantics of whether you use the niceties of C++ like a vector
of struct for rectangles and whether you use a string
instead of char*
, etc. However, there are certain benefits of using a string
to read/validate the remainder of each line (or you could check the stream state and use .clear()
and .ignore()
)
Putting those pieces together, you can do something like the following. Note, there are many, many different approaches you can take, this is just one approach,
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
typedef struct { /* simple typedef for vect of rectangles */
int width, height;
} rect_t;
int main (int argc, char **argv) {
vector<double> cir; /* vector of double for circle radius */
vector<double> tri; /* vector of double for triangle side */
vector<rect_t> rect; /* vector of rect_t for rectangles */
string line; /* string to use a line buffer */
if (argc < 2) { /* validate at least one argument given */
cerr << "error: insufficient input.\n"
"usage: " << argv[0] << " filename\n";
return 1;
}
ifstream f (argv[1]); /* open file given by first argument */
if (!f.is_open()) { /* validate file open for reading */
cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1;
}
while (getline (f, line)) { /* read each line into 'line' */
string shape; /* string for shape */
istringstream s (line); /* stringstream to parse line */
if (s >> shape) { /* if shape read */
if (shape == "c") { /* is it a "c"? */
double r; /* radius */
string rest; /* string to read rest of line */
if (s >> r && !getline (s, rest)) /* radius & nothing else */
cir.push_back(r); /* add radius to cir vector */
else /* invalid line for circle, handle error */
cerr << "error: invalid radius or unexpected chars.\n";
}
else if (shape == "t") {
double l; /* side length */
string rest; /* string to read rest of line */
if (s >> l && !getline (s, rest)) /* length & nothing else */
tri.push_back(l); /* add length to tri vector */
else /* invalid line for triangle, handle error */
cerr << "error: invalid triangle or unexpected chars.\n";
}
else if (shape == "r") { /* is it a rect? */
rect_t tmp; /* tmp rect_t */
if (s >> tmp.width && s >> tmp.height) /* tmp & nohtin else */
rect.push_back(tmp); /* add to rect vector */
else /* invalid line for rect, handle error */
cerr << "error: invalid width & height.\n";
}
else /* line neither cir or rect, handle error */
cerr << "error: unrecognized shape '" << shape << "'.\n";
}
}
cout << "\nthe circles are:\n"; /* output valid circles */
for (auto& i : cir)
cout << " c: " << i << "\n";
cout << "\nthe triangles are:\n"; /* output valid triangles */
for (auto& i : tri)
cout << " t: " << i << "\n";
cout << "\nthe rectangles are:\n"; /* output valid rectangles */
for (auto& i : rect)
cout << " r: " << i.width << " x " << i.height << "\n";
}
By storing values for your circles, triangles and rectangles independent of each other, you then have the ability to handle each type of shape as its own collection, e.g.
Example Use/Output
$ ./bin/read_shapes dat/shapes.txt
error: unrecognized shape '3'.
error: unrecognized shape '10'.
the circles are:
c: 12
c: 2
c: 2.4
the triangles are:
t: 2.9
t: 2.9
the rectangles are:
r: 3 x 4
Look things over and let me know if you have further questions. The main takeaway is to insure you validate down to the point you can insure what you have read is either a round-peg to fit in the circle hole, a square-peg to fit in a square hole, etc..