简体   繁体   中英

C++ fstream, reading data from text and preforming math operations?

I am at my wits end here. I am writing a program in C++ that will read a text file that contains the following as a brief example:

+ 23 34
- 9 8
+ 100 1
* 8 7
^ 2 5
/ 45 8

The reader will store the first operands in a char type and based on the char it retrieves, a function will be called to preform the operation on the two numbers, here's a sample functions that will do this.

void doDivision(ifstream &inFile) {
    char ch;
    int num1, num2;

    inFile >> ch >> num1 >> num2;
    cout << "Division     " << num1 << "    " << num2 << "    " << "Quotient " << "    " << num1/num2 << " Remain " << num1%num2  << endl; 
}

One thing is I am not sure why the argument is &inFile this function prototype is not made by me, but it is from a book maybe this is why I cannot I get it to work.

Here is my main function:

int main()
{

ifstream inFile;
char ch;
int num1, num2;

inFile.open("math.txt");
if (inFile.fail())
{
cout << ch;
      cout << "The math.txt input file failed to open";
        return -1;
}


 while(inFile)
{
    switch (ch) {

    case '+':
        doAddition(inFile);
        break;
    case '-':
        doSubtraction(inFile);
        break;
    case '*':
        doMultiplication(inFile);
        cout << "debug " << ch;
        break;
    case '/':
        doDivision(inFile);
        break;
    case '!':
        doFactorial(inFile);
        break;
    default:
        cout << "Invalid Operation" << endl;

    }

    inFile >> ch;
}

inFile.close();
return 0;
}

All of this together produces the following unintended result

Invalid Operation 
Addition 3 34 sum 37 (wrong data in text file is 23 and 34)
subtraction 0 8 difference 8 (data in textfile is 9 and 8 respectively)

How would I implement this, I am overwhelmed since I have never worked with files before.

One simple error is that inside of your while() loop, you don't call inFile >> ch until after the first time through the loop. Try fixing that and see if it helps.

Also, what Aniket said in their answer is another problem you need to look out for.

So in short, your loop should look roughly like this:

inFile >> ch;
while(inFile) {
    switch(ch) {
    case '+':
        ...
    }
    inFile >> ch;
}

And your functions should be similar to this:

void doDivision(ifstream &inFile) {
    int num1, num2;

    inFile >> num1 >> num2;

    ...
}

Since you are already doing inFile >> ch in main() it will be wrong(logically) to read it again in doAddition() and other methods.

since you've posted doDivision() method where you read in the ch character, I am assuming you're doing the same in doAddition() as well.

Also your output:

Addition 3 34 sum 37 (wrong data in text file is 23 and 34)

Tells us all - you've read 1 character(found + ) then read another character in doAddition() method, - which read 2 into ch of doAddition() then read 3 and 34 ..

This is where the error actually is.

Solution: change your doAddition() and all other functions to something like below.

void doAddition(ifstream &inFile) {
    char ch;
    int num1, num2;

    inFile >> num1 >> num2;
    cout << "Addition of " << num1 << " and " << num2 << " = "<< (num1+num2) << '\n';
}

Also, in the main() function:

the while loop should look like this:

while(inFile)
{
    inFile >> ch;
    switch (ch) {

    case '+':
        doAddition(inFile);
        break;
    case '-':
        doSubtraction(inFile);
        break;
    case '*':
        doMultiplication(inFile);
        cout << "debug " << ch;
        break;
    case '/':
        doDivision(inFile);
        break;
    case '!':
        doFactorial(inFile);
        break;
    default:
        cout << "Invalid Operation" << endl;

    }
}

You do not read into ch for the first iteration. In fact, when you check that your file opened with no problem, you do cout << ch; despite the fact that ch is uninitialized. You could simply move the inFile >> ch; to the top of the while loop.

The next problem is that inside each of your doSomething functions, you are doing inFile >> ch again, which will attempt to read the operation character again. Your doSomething functions don't need to know ch though, since the function itself was chosen based on the value of ch .

Here is how I would write this:

ifstream inFile("math.txt"); // You can specify the file name here
char op;
int left_operand, right_operand;

// Use extraction has while condition
while (inFile >> op >> left_operand >> right_operand) {
  switch (op) {
    // Pass operands to the relevant function
    case '+': doAddition(left_operand, right_operand); break;
    // ...
  }
}

// You do not need to close inFile, it will be closed when it goes out of scope
return 0;

Ideally, there are better ways of doing what you suggest, but if you have to make your code look and perform like this, then the appropriate solution should be something of this sort:

#include <vector>
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

void doAddition(ifstream &inFile) {
    int num1, num2;

    inFile >> num1 >> num2;
    cout << "Addition of " << num1 << " and " << num2 << " = "<< (num1+num2) << '\n';   
}

void doSubtraction(ifstream &inFile) {
    int num1, num2;

    inFile >> num1 >> num2;
    cout << "Subtraction of " << num1 << " and " << num2 << " = "<< (num1-num2) << '\n';
}

void doMultiplication(ifstream &inFile) {
    int num1, num2;

    inFile >> num1 >> num2;
    cout << "Multiplication of " << num1 << " and " << num2 << " = "<< (num1*num2) << '\n';
}

void doDivision(ifstream &inFile) {
    float num1, num2;

    inFile >> num1 >> num2;
    cout << "Division of " << num1 << " and " << num2 << " = "<< (num1/num2) << '\n';
}

void doFactorial(ifstream &inFile) {
    int t1, t2;

    inFile >> t1 >> t2;

    //perform factorial here
}

void readToNextLine(ifstream& inFile) {
    string t1, t2;

    inFile >> t1 >> t2;
}

int main()
{
    ifstream inFile;
    char ch;
    int num1, num2;

    inFile.open("math.txt");

    if (inFile.is_open()){

        inFile >> ch;
        while (!inFile.eof())
        {
            switch (ch) 
            {

                case '+':
                    doAddition(inFile);
                    break;
                case '-':
                    doSubtraction(inFile);
                    break;
                case '*':
                    doMultiplication(inFile);
                    break;
                case '/':
                    doDivision(inFile);
                    break;
                case '!':
                    doFactorial(inFile);
                    break;
                default:
                    readToNextLine(inFile);
                    cout << "Invalid Operation" << endl;
            }
            inFile >> ch;
        }
            inFile.close();
    }
    else
    {
        cout << "The math.txt input file failed to open";
        return -1;
    }

    inFile.close();
    return 0;
}

A couple things to note here:

Some of the solutions that the others suggested, do not consider the default case - which simply forgoes reading the rest of the line, resulting in logic errors - which no one wants.

Now, as for a "better", more general solution, it would be more ideal to store everything as strings first, then tokenize, and attempt to convert the appropriate token to the desired type output.

But, as I noted before, if you wish to conform to the code you had previously, then this is appropriate.

Unless you're feeling masochistic, it's almost certainly easiest to read the operator and two operands all at once, then carry out the operation:

char operation;
int operand1, operand2;

while (infile >> op >> operand1 >> operand2) 
   switch(operation) {
       case '+': add(operand1, operand2); break;
       case '-': sub(operand1, operand2); break;
       case '*': mul(operand1, operand2); break;
       case '/': div(operand1, operand2); break;
   }

For only four operators, this works well enough as-is. If you're going to have many more, you'd probably be better off using a table of pointers to functions instead:

typedef int (*op)(int, int);

op operators[UCHAR_MAX];

for (int i=0; i<UCHAR_MAX; i++)
    operators[i] = report_bad_operator;

operators['+'] = add;
operators['-'] = sub;
operators['/'] = div;
operators['*'] = mul;
// more operators here

while (infile >> operation >> operand1 >> operand2)
    operators[operation](operand1, operand2);

The obvious reason/time to do otherwise would be to deal with non-binary operators (ie, ones that don't necessarily take exactly two operands).

First of all, you are reading chr, before assigning a value to it.

Change your while loop like this:

while(inFile)
{
    inFile >> ch; // assign before reading
    switch (ch) {

    case '+':
        doAddition(inFile);
        break;
    case '-':
        doSubtraction(inFile);
        break;
    case '*':
        doMultiplication(inFile);
        cout << "debug " << ch;
        break;
    case '/':
        doDivision(inFile);
        break;
    case '!':
        doFactorial(inFile);
        break;
    default:
        cout << "Invalid Operation" << endl;
    }
}

And change your doDivision function like this:

void doDivision(ifstream &inFile)
{ 
    int num1, num2;

    inFile >> num1 >> num2; // your already read the arithmetic operator
    cout << "Division     " << num1 << "    " << num2 << "    " << "Quotient " << " 
}

First, initial pass, your ch variable is not initialized. It is initialized at the bottom of the loop.

Try adding:

 inFile >> ch;

before the while loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM