i'm stuck on a program that i am working on in class. your suppose to open a text file with names of shapes and certain lengths of it's sides. it then calculates what it's area/perimeter/volume/circumference and so on and so forth for each shape named in the text file.

i tokenized the input file and was able to print on screen. now i am stuck trying to figure out how to manipulate the tokens. i am not sure how i can manipulate the tokens, maybe array?

here is what i have so far:

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

#include <fstream>
using std::ifstream;

#include <cmath>

#include <cstring>
using std::string;
using std::strtok;
using std::strncmp;

#include <cstdlib>

const int MAX_CHARS_PER_LINE = 50;
const int MAX_TOKENS_PER_LINE = 4;
const char* DELIMITER = " ";

int main()
{
  cout << "Description: Calculates area, perimeter, surface area, and volume" << endl;
  cout << "of 6 different geometric objects." << endl;
  cout << endl;

  ifstream fin;
  fin.open("geo.txt");
  if(!fin.good())
    return 1;

  char* token[MAX_TOKENS_PER_LINE] = {0};

  while(!fin.eof())
  {
    char buf[MAX_CHARS_PER_LINE];
    fin.getline(buf, MAX_CHARS_PER_LINE);

    int n = 0;

    token[0] = strtok(buf, DELIMITER);
    if(token[0])
    {
      for(n = 1; n < MAX_TOKENS_PER_LINE; n++)
      {
        token[n] = strtok(0, DELIMITER);
        if(!token[n]) break;
      } //for
    } //if

    for(int i = 0; i < n; i++)
    {
      cout << "Token[" << i << "] = " << token[i] << endl;
    }

    cout << endl;
  } //while

  cout << endl;
  cout << "Press Enter to continue..." << endl;
  cin.get();

  return 0;
} //main

here is the input file:

SQUARE 14.5
RECTANGLE 14.5    4.65
CIRCLE 14.5
CUBE 13
PRISM 1 2 3

SPHERES 2.4
CYLINDER 1.23
CYLINDER 50 1.23
TRIANGLE 1.2 3.2

the output should look something like this:

SQUARE side=14.5 area=210.25 perimeter=58.00
RECTANGLE length=14.5 width=4.65 area=67.43 perimeter=38.30
CIRCLE radius=14.5 area=660.52 circumference=91.11
CUBE side=13 surface area=1014.00 volume=2197.00
PRISM length=1 width=2 height=3 surface area=22.00 volume=6.00
SPHERES invalid object
CYLINDER radius=1.23 height=0 surface area=9.51 volume=0.00
CYLINDER radius=50 height=1.23 surface area=16094.37 volume=9660.39
TRIANGLE invalid object

and for any objects with no sides it gives invalid object.

can anyone point me in the right direction.

Figure out what have. Figure out what you need. Figure out what you need to store. Decide what assumptions you can make.

You have a different number of tokens required for each shape. Think of it as a key-value pair, where the value is the NUMBER of values.

So set up a struct.

struct key_num_tokens
{
    char key[100];
    unsigned int num_tokens;
};

Now fill it in...

key_num_tokens shape_tokens[] =
{
    {"SQUARE", 1},
    {"RECTANGLE", 2},
    {"CIRCLE", 1},
    etc.
};

Now you set up another struct to actually HOLD the tokens as you read them in.

struct shape_token
{
    char key[100];
    double values[MAX_TOKENS_PER_LINE];
}

Once you have a shape_token object, you can actually do the calculation. When you read a line. you read in the key, then you go through the shape_tokens[] array to figure out how many values you need for that key, then read in the line. The line needs to have exacly that many values after the key.

For each line...

  1. Read in the key
  2. Make sure it's a valid key.
  3. Do a lookup from the key to the number of tokens you need.
  4. Read in and store the tokens. Verify that you have the correct number.
  5. Do the calculation. The key gives you the formula to use. The values are what you plug into the formula.

Edited 5 Years Ago by VernonDozier: n/a

It looks like you are doing the right thing if you want to completely process the shape as you read it.
There are other options:
1) create a class for each shape and if the string contains the name of the class, feed the entire string to the class and let the constructor parse it. The class should contain the functions for determining the particulars of each shape-type, so all you would have to call is something like cout << mySquare->GetPerimeter();

2) Make individual methods for each shape type, so as you PARSE the strings, you feed the values to the function and get the return value like: cout << GetSquarePerimeter(14.5)

In fact, if you've gotten as far as inheritance in your class, think about what's shared across all shapes: they take a set of tokens, determine whether the tokens are valid (as Vernon started you out with), and they print out some info. As long as the specific info isn't needed anywhere except inside the displayInfo() method, you can derive each shape from a base class Shape:

class Shape
{
protected:
    bool valid;
public:
    Shape(char *tokens[]);
    bool isValid();
    void displayInfo();
}

class Circle: public Shape
{
protected:
    double radius;
public:
    Circle(char *tokens[]);
    double Circumference();
    double Area();
    // isValid and displayInfo are already declared by Shape, we just re-implement them here
};

Circle::displayInfo()
{
    cout << "CIRCLE ";
    if isValid() {
        double circumference = Circumference();
        double area = Area();
        cout << "radius=" << radius << " area=" << area
             << " circumference=" << circumference << endl;
    }
    else
        cout << "invalid object" << endl;
}

If you haven't covered inheritance yet, don't worry about all this, just check whether the first token is a recognized shape-name, and if so, do the right thing with the remaining tokens.

couldn't i store the tokens first then see if whatever is stored to be valid, and once it checks out good it'll take the next 1,2,3 tokens for whatever object and send it to a function to calculate the measurements?

couldn't i store the tokens first then see if whatever is stored to be valid, and once it checks out good it'll take the next 1,2,3 tokens for whatever object and send it to a function to calculate the measurements?

You can do whatever you like. You can read everything in, store it all, THEN parse, parse as you read, parse the first key, then figure out from that how many tokens you need to read in, then read that many, read in a line at a time, then parse that line into tokens, etc. I imagine it's a matter of personal preference, plus thinking ahead to what you might be doing with this later, etc. Probably if ten more people weighed in, you'd get ten new ideas.

For me, I say why bother using strtok if I can use the >> operator and I can read a value directly into a double, and why bother reading in a bunch of tokens, then checking them when I can just figure out how many I'm supposed to have first and read that many in directly with a loop. All this depends on how "well-formed" you can assume your input file is. For all I know your instructor is going to throw in all sorts of curveballs that you have to make sure you catch and don't crash your program.

couldn't i store the tokens first then see if whatever is stored to be valid, and once it checks out good it'll take the next 1,2,3 tokens for whatever object and send it to a function to calculate the measurements?

Of course you can. I was offering up one potential design for "a function that would take the next 1,2,3 tokens for the object, and calculate the measurements." In an object-oriented paradigm, an instance of a particular class would be responsible for that functionality -- the constructor (method with the same name as the class) would take the tokens, assign them to meaningful member variables, and maybe determine as part of that process whether there are the correct number of tokens and each is the expected type; separate functions that compute the desired output values for that shape can be instance methods; and printing out the resulting line can be another instance method.

There is additional thought required as far as which object (or function) is responsible for reading the input and determining which kind of shape-object to create (possibly a factory class), and how it knows how many tokens the shape-object needs (possibly a class method for the specific shape). All of this can be done with relatively simple data structures and non-object-oriented functions, but then why use C++? Of course, it also depends on what material your class has covered, and what the expectations for the assignment are, other than producing the correct output for the given input.

This article has been dead for over six months. Start a new discussion instead.