0

I'll preface this by noting that I'm very new to C++ and programming in general so if I'm doing something improperly or writing code in an odd way, it's because i've only learned so much so far.

Anyways, I was given an assignment to write a program that first

  • Reads integers from a file of the user's choice
    • Outputs the sum and average of all the numbers greater than or equal to zero ,
    • Outputs the sum and average of all the numbers less than zero
    • Outputs the sum and average of all the numbers, whether positive, negative, or zero.
    • Then finally asks the user if they'd like to choose another file to run on again

The only catch is that I have to use a dynamic array within the code, I'm assuming in order to allow the file to hold any amount of integers.

So far, I have everything except for the implementation of the dynamic array. The code is currently programmed to only accept 10 integers (as there are no arrays in the code yet).

Here's my code:

#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

int main() {

    //Variables
    string inFile;
    int numbers, i = 0;
    double avg, neg_avg, total_sum, total_avg, sum = 0, neg_sum = 0;;
    double count = 0, neg_count = 0, pos_count = 0;
    char answer;



    do
    {
        //Input Question
        cout << "Enter the file name.\n";
        cin >> inFile;  // Input from User
        ifstream fin;   // Open File
        fin.open(inFile);

        if (fin.fail())  // Check to see if file opens properly
        {
            cout << "An error occurred while attempting to open the file.\n";
            exit(1);
        }

        while (count < 10)
        {
            fin >> numbers;
            if (numbers >= i)
            {
                sum += numbers;
                count += 1;
                pos_count += 1;
            }
            if (numbers < i)
            {

                neg_sum = neg_sum + numbers;
                count = count + 1;
                neg_count = neg_count + 1;
            }
        }


        //Calculations

        avg = sum / pos_count;
        neg_avg = neg_sum / neg_count;
        total_sum = sum + neg_sum;
        total_avg = total_sum / 10.0;


        //OUTPUT
        cout << "The sum of all positive numbers is: " << sum << endl;
        cout << "The average of all positive numbers is: " << setprecision(3) << avg << endl;
        cout << "The sum of all negative numbers is: " << neg_sum << endl;
        cout << "The average of all negative numbers is: " << setprecision(3) << neg_avg << endl;
        cout << "The sum of all numbers is: " << total_sum << endl;
        cout << "The average of all numbers is: " << setprecision(3) << total_avg << endl;

        cout << "-------------------------------------------------" << endl;

        cout << "Want us to read another file?\n";
        cout << "Enter 'Y' or 'y'  for yes, any other character for no." << endl;
        cin >> answer;
    } while ((answer == 'y') || (answer == 'Y'));

    return 0;


}

Any help would be greatly appreciated! Thanks in advance

UPDATE:

I've gotten this far but when I compile, the program runs continuously.Not sure what I'm doing wrong.

#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

int main() {

    //Variables
    string file;
    int i = 0;
    double avg, neg_avg, total_sum, total_avg, sum = 0, neg_sum = 0;;
    double neg_count = 0, pos_count = 0, totcount = 0;
    char answer;

    //Input Question

    do
    {
        cout << "Enter the file name.\n";
        cin >> file;  // Input from User
        ifstream fin;   // Open File
        fin.open(file);

        if (fin.fail())  // Check to see if file opens properly
        {
            cout << "An error occurred while attempting to open the file.\n";
            exit(1);
        }

        while (!fin.eof())
        {
            int numbers;
            fin >> numbers;
            int *dynamicArray;
            dynamicArray = new int[numbers];

            if (numbers >= i)
            {
                sum += numbers;
                pos_count += 1;
                totcount += 1;

            }
            if (numbers < i)
            {
                neg_sum = neg_sum + numbers;
                neg_count = neg_count + 1;
                totcount += 1;
            }

            //Calculations

            avg = sum / pos_count;
            neg_avg = neg_sum / neg_count;
            total_sum = sum + neg_sum;

            total_avg = total_sum / totcount;


            //OUTPUT
            cout << "The sum of all positive numbers is: " << sum << endl;
            cout << "The average of all positive numbers is: " << setprecision(3) << avg << endl;
            cout << "The sum of all negative numbers is: " << neg_sum << endl;
            cout << "The average of all negative numbers is: " << setprecision(3) << neg_avg << endl;
            cout << "The sum of all numbers is: " << total_sum << endl;
            cout << "The average of all numbers is: " << setprecision(3) << total_avg << endl;
            cout << "-------------------------------------------------" << endl;

            delete [] dynamicArray;

        }

        fin.close();

        cout << "Want us to read another file?\n";
        cout << "Enter 'Y' or 'y'  for yes, any other character for no." << endl;
        cin >> answer;
    } while ((answer == 'y') || (answer == 'Y'));

    return 0;


}

UPDATE:

#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
using namespace std;

int main() {

    //Variables
    string file;
    int i = 0, value = 0, e = 0;
    double avg, neg_avg, total_sum, total_avg, sum = 0, neg_sum = 0;;
    double neg_count = 0, pos_count = 0, totcount = 0;
    char answer;

    //Input Question

    do
    {
        cout << "Enter the file name.\n";
        cin >> file;  // Input from User
        ifstream fin;   // Open File
        fin.open(file);

        if (fin.fail())  // Check to see if file opens properly
        {
            cout << "An error occurred while attempting to open the file.\n";
            exit(1);
        }


        //                       <----------  This works to get the size of the file
        int elements;
        vector<int> eCount;
        while (fin >> elements)
        {
            eCount.push_back(elements);
        }
        int size = static_cast<int> (eCount.size());
        cout << "size = " << size << endl;//  <-----------Test to see if working 

        //From this point, size of the file is held in the variable, 'size'.

        int array_size = size;
        int* p;
        p = new int[array_size];





        int location = 0;
        while (!fin.eof())
        {
            fin >> p[location];
            location++;
        }

        cout << "P[12] is equal to " << p[12] << endl;// <----Test to see if array is initialized

        while (fin >> p[location])
        {


            if (p[e] >= i)
            {
                sum = sum + p[location];
                pos_count = pos_count + 1;
                totcount = totcount + 1;
            }
            else 
            {
                neg_sum = neg_sum + p[location];
                neg_count = neg_count + 1;
                totcount = totcount + 1;
            }
            location++;
        }
        //Calculations

        avg = sum / pos_count;
        neg_avg = neg_sum / neg_count;
        total_sum = sum + neg_sum;

        total_avg = total_sum / totcount;

        fin.close();

        //OUTPUT
        cout << "The sum of all positive numbers is: " << sum << endl;
        cout << "The average of all positive numbers is: " << setprecision(3) << avg << endl;
        cout << "The sum of all negative numbers is: " << neg_sum << endl;
        cout << "The average of all negative numbers is: " << setprecision(3) << neg_avg << endl;
        cout << "The sum of all numbers is: " << total_sum << endl;
        cout << "The average of all numbers is: " << setprecision(3) << total_avg << endl;
        cout << "-------------------------------------------------" << endl;







        cout << "Want us to read another file?\n";
        cout << "Enter 'Y' or 'y'  for yes, any other character for no." << endl;
        cin >> answer;
    } while ((answer == 'y') || (answer == 'Y'));

        return 0;
}

Thank you to everyone who's pitched in. I wish I didn't have to use a dynamic array but unfortunately I won't receive if I don't implement one. I updated my code but I can't seem to get the array working properly as it does not seem to load the input from the file properly. Anything helps!

JATX99
  • 17
  • 3
  • In C++, dynamic array type is spelled `std::vector` – Igor Tandetnik Nov 06 '19 at 05:47
  • *"The program runs continually?"* See [Why !.eof() inside a loop condition is always wrong.](https://stackoverflow.com/q/5605125/9254539) You will want to review [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) AND with `char answer;` what happens to the `'\n'` left in `stdin` by `cin >> file;`?? – David C. Rankin Nov 06 '19 at 07:49
  • Why do you think you need an array at all? You can simply keep a running sum of positive, negative, and all numbers as well as the count of each and then compute the average for each at the end. As written you are not using your array at all. – David C. Rankin Nov 06 '19 at 08:01
  • If i'm incorrectly using the dynamic array, (very likely) it makes sense for why the program isn't functioning properly. I could easily get away without using an array but unfortunately it's mandatory. – JATX99 Nov 06 '19 at 08:20
  • Also thanks for the tip with cin. I'll will add a clear. – JATX99 Nov 06 '19 at 08:21

2 Answers2

1

Well, the biggest I/O problem you have is attempting to read with while (!fin.eof()). See Why !.eof() inside a loop condition is always wrong.. The biggest logic problem you are having is including the //Calculations in the same loop you are reading your integers from your file.

Since you read and integer and are keeping a running sum of the positive and negative values, there is no need for a dynamic array at all. You are currently keeping pos_count, neg_count, and totcount which are all you need to compute the respective averages when you leave your read loop.

To tidy things up a bit, let's look at your variables. While you can use double for pos_count, neg_count, and totcount, it's better to use an unsigned type for a counter. C++ provides size_t as a preferred sizetype for counts and lengths, but it's not mandatory -- it just makes sense. While you can use a separate file and answer, it is better to read each input into a std::string to ensure a single keystroke (like the user typing "Yes" instead of 'Y') doesn't leave additional characters unread in stdin. You can also use the same std::string for both your file and answer and just check the if the first character is 'y' or 'Y' to control your read another file loop.

Putting that together, your variables could simple be:

int main (void) {

    std::string buffer;     /* use single buffer for filename & answer */

    do
    {   // Variables   (will be reinitialized for each file)
        int number;             /* you are reading one number at a time */
        size_t  neg_count = 0, pos_count = 0, totcount = 0;
        double  avg, neg_avg, total_sum, total_avg, sum = 0., neg_sum = 0.;

(note: buffer to read the answer into is the only variable that must be declared before your do {...} while(); loop to be used as the test condition at the end)

If you remember nothing else, remember to validate every input, e.g.

        std::cout << "Enter the file name: ";
        if (!(std::cin >> buffer)) {            // VALIDATE Input from User
            std::cerr << "(user canceled input)\n";
            return 1;
        }

While you can check if the .fail() bit is set on the stream, a more general test is if the file stream goodbit is not set, e.g.

        std::ifstream fin(buffer);              // open file stream

        if (!fin.good()) {      // Check to see if file opens properly
            std::cerr << "error: file open failed - " << buffer << ".\n";
            return 1;
        }

(note: either way will work)

When you read in a loop, condition your loop on a successful read. Your read loop here needs to be nothing more than:

        while (fin >> number) { /* control read loop with read itself */
            if (number >= 0) {  /* handle positive numbers */
                sum += number;
                pos_count += 1;
            }
            else {              /* if it's not >= 0, it's negative */
                neg_sum = neg_sum + number;
                neg_count = neg_count + 1;
            }
            totcount += 1;      /* total count incremented each time */
        }
        fin.close();

That will capture all the information you need from your file. Now do the average calculations, but what happens if pos_count, neg_count, or totcount == 0. Dividing by zero is generally a really, really bad thing. Always validate your denominator, e.g.

        // Calculations
        if (pos_count > 0)
            avg = sum / pos_count;
        else
            avg = 0;

        if (neg_count > 0)
            neg_avg = neg_sum / neg_count;
        else
            neg_avg = 0;

        total_sum = sum + neg_sum;
        if (totcount > 0)
            total_avg = total_sum / totcount;
        else
            total_avg = 0;

Now for your output. How many times do you want to call cout for one continual block of output? (hint: once)

        //OUTPUT    (you only need one std::cout)
        std::cout << "\nThe sum of all positive numbers is: " 
                << sum << std::endl
                << "The average of all positive numbers is: " 
                << std::setprecision(3) << avg << std::endl 
                << "The sum of all negative numbers is: " 
                << neg_sum << std::endl 
                << "The average of all negative numbers is: " 
                << std::setprecision(3) << neg_avg << std::endl 
                << "The sum of all numbers is: " << total_sum << std::endl 
                << "The average of all numbers is: " << std::setprecision(3) 
                << total_avg << std::endl 
                << "-------------------------------------------------\n\n" 
                << "Want to read another file?\n" 
                << "Enter 'Y' or 'y'  for yes, any other character for no.\n";

That handles all your output needs in a single call (including your prompt for 'Y' or 'y'). Now just use the same std::string to take input on whether to continue or not, e.g.

        if (!(std::cin >> buffer)) {
            std::cerr << "(user canceled input)\n";
            return 1;
        }
        /* condition on 1st char in buffer */
    } while ((buffer.at(0) == 'y') || (buffer.at(0) == 'Y'));
}

That's it you are done. Putting it altogether, and replacing the fragile use of std::cin >> buffer with getline (std::cin, buffer) you would have:

#include <iostream>
#include <fstream>
#include <iomanip>

int main (void) {

    std::string buffer;     /* use single buffer for filename & answer */

    do
    {   // Variables   (will be reinitialized for each file)
        int number;             /* you are reading one number at a time */
        size_t  neg_count = 0, pos_count = 0, totcount = 0;
        double  avg, neg_avg, total_sum, total_avg, sum = 0., neg_sum = 0.;

        std::cout << "Enter the file name: ";
        if (!getline(std::cin, buffer)) {       // VALIDATE Input from User
            std::cerr << "(user canceled input)\n";
            return 1;
        }
        std::ifstream fin(buffer);              // open file stream

        if (!fin.good()) {      // Check to see if file opens properly
            std::cerr << "error: file open failed - " << buffer << ".\n";
            return 1;
        }

        while (fin >> number) { /* control read loop with read itself */
            if (number >= 0) {  /* handle positive numbers */
                sum += number;
                pos_count += 1;
            }
            else {              /* if it's not >= 0, it's negative */
                neg_sum = neg_sum + number;
                neg_count = neg_count + 1;
            }
            totcount += 1;      /* total count incremented each time */
        }
        fin.close();

        // Calculations
        if (pos_count > 0)
            avg = sum / pos_count;
        else
            avg = 0;

        if (neg_count > 0)
            neg_avg = neg_sum / neg_count;
        else
            neg_avg = 0;

        total_sum = sum + neg_sum;
        if (totcount > 0)
            total_avg = total_sum / totcount;
        else
            total_avg = 0;

        //OUTPUT    (you only need one std::cout)
        std::cout << "\nThe sum of all positive numbers is: " 
                << sum << std::endl
                << "The average of all positive numbers is: " 
                << std::setprecision(3) << avg << std::endl 
                << "The sum of all negative numbers is: " 
                << neg_sum << std::endl 
                << "The average of all negative numbers is: " 
                << std::setprecision(3) << neg_avg << std::endl 
                << "The sum of all numbers is: " << total_sum << std::endl 
                << "The average of all numbers is: " << std::setprecision(3) 
                << total_avg << std::endl 
                << "-------------------------------------------------\n\n" 
                << "Want to read another file?\n" 
                << "Enter 'Y' or 'y'  for yes, any other character for no.\n";
        if (!getline(std::cin, buffer)) {
            std::cerr << "(user canceled input)\n";
            return 1;
        }
        /* condition on 1st char in buffer */
    } while ((buffer.at(0) == 'y') || (buffer.at(0) == 'Y'));
}

(note: getline (std::cin, buffer) has been used in the code above to make the user input a bit more robust -- see the the section below the example output for the reasons)

Example Use/Output

Testing with three files, the first a 50x5 set of positive integers, then next a set of 10 integers with one negative value (-2213) and the last a file of 100 mixed positive and negative values would give:

$ ./bin/pos_neg_total
Enter the file name: dat/50x5.txt

The sum of all positive numbers is: 122180
The average of all positive numbers is: 489
The sum of all negative numbers is: 0
The average of all negative numbers is: 0
The sum of all numbers is: 1.22e+05
The average of all numbers is: 489
-------------------------------------------------

Want to read another file?
Enter 'Y' or 'y'  for yes, any other character for no.
y
Enter the file name: ../../..//src-c/tmp/dat/10int_nl.txt

The sum of all positive numbers is: 2.03e+05
The average of all positive numbers is: 786
The sum of all negative numbers is: -2.21e+03
The average of all negative numbers is: -2.21e+03
The sum of all numbers is: 2.01e+05
The average of all numbers is: 774
-------------------------------------------------

Want to read another file?
Enter 'Y' or 'y'  for yes, any other character for no.
Y
Enter the file name: ../../../src-c/tmp/dat/100int.txt

The sum of all positive numbers is: 1.93e+06
The average of all positive numbers is: 5.55e+03
The sum of all negative numbers is: -2.29e+05
The average of all negative numbers is: -1.76e+04
The sum of all numbers is: 1.7e+06
The average of all numbers is: 4.71e+03
-------------------------------------------------

Want to read another file?
Enter 'Y' or 'y'  for yes, any other character for no.
n

There are many, many ways to put this together, and you are free to use as many variables or calls to std::cout as you like, but hopefully this will help you think further along the lines of "What does my program require?".

Using >> for User-Input is Fragile

As a final note, know that using std::cin >> string for user-input is horribly fragile as any whitespace the user types as part of the input will not be read (and it will be left unread in stdin. It is far better to use getline which will read the complete line into your string. Don't mix using the >> iostream for input with getline without accounting for any '\n' that may be left in stdin. You can then use std::cin.ignore() to clear. In your case it would simply be more robust to take all user input with getline, e.g.

        if (!getline(std::cin, buffer)) {          // VALIDATE Input from User
            std::cerr << "(user canceled input)\n";
            return 1;
        }

Then filenames with whtespace would be properly handled, and if the user wanted to type "Yes I want to enter another file!" as his answer to your continue question, that would pose no problem at all. If you haven't got there yet in your class, put it in your hip-pocket. For an experiment, try replacing both user inputs with what is shown above with your original std::cin >> buffer and see what happens if you type "Yes I want to enter another file!" at the prompt :)

Let me know if you have further questions.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
0

So why you need a vector(dynamic array) to store the integers since your code can handle the all cases by add a "break" expression on the condition of EOF. If you really need it, below is what you need:

#include<vector>
vector<int> my_vec;
  • Thanks for the tip with the vectors. Although that's probably the best solution (and they're basically dynamic arrays), I think we actually have to use a dynamic array unfortunately. – JATX99 Nov 06 '19 at 07:25