1

I was tasked to read 3 rows of 5 comma separated values from a text file, sum up each column, and store the result in an array called bins. I am struggling to read the ints from the text file as they are comma separated. I first need to clarify how to read just the ints.

My next thought was to store the ints from the file into an array called "calc", and use the index of each element to sum up the values. I would then store these results into the "bins" array.

Here is some code I have tried to read the comma separated ints yet I cannot seem to get it to work.

int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;
int calc[15] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};

ifstream myfile;
myfile.open("values.txt");

for(int i = 0; i <= 15; i++)
{
    myfile >> calc[i];

    myfile.close();
    a = calc[0];
    b = calc[1];
    c = calc[2];
    d = calc[3];
    e = calc[4];
    f = calc[5];
    g = calc[6];
    h = calc[7];
    i = calc[8];
    j = calc[9];
    k = calc[10];
    l = calc[11];
    m = calc[12];
    n = calc[13];
    o = calc[14];

    cout << calc[i] << endl;
}

I am really new to working with code and I dont quite understand how to work with values in this manner. It is a simple task yet I cannot seem how to implement it with code.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
Consa
  • 11
  • 5
  • `i <= 15` [off-by-one error](https://en.wikipedia.org/wiki/Off-by-one_error) here. – MikeCAT May 21 '21 at 13:29
  • The `int calc[15] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};` looks a bit suspicious. Why not `int calc[15];` or `int calc[15] = { };`. What you did is to initialize the 15 array elements with 15 uninitialized variables `a` ... `o`. I'm afraid that leaves the array still somehow "uninitialized" as before. – Scheff's Cat May 21 '21 at 13:41
  • @Scheff they are in global scope, so they are default-initialized, i.e, 0. But otherwise that code smells. it's enough to have `int calc[15] = {};` – Swift - Friday Pie May 21 '21 at 13:43
  • Yet another doubt: If you run a loop to read `calc[i]` for i = 0 ... 14 (assuming you fixed the wrong condition as recommended by @MikeCAT), for what is the assignment of `a` ... `o` good? Imagine the first iteration (when `i == 0`): You will read `calc[0]` and all the read accesses to `calc[1]` ... `calc[14]` are for... what? – Scheff's Cat May 21 '21 at 13:44
  • 1
    @Swift-FridayPie _they are in global scope_ Not sure. I assume there is something left out here. Statements like `for` may not be used in global scope. (They may appear only in any kind of function body.) – Scheff's Cat May 21 '21 at 13:45
  • 1
    Please, [edit] and provide a [mcve]. Otherwise, it won't last long until somebody tells you this just doesn't compile as is... ;-) – Scheff's Cat May 21 '21 at 13:46
  • @Scheff ok, I guess , I'm half-asleep and saw a ghost of main's body there.. need more programmer's fuel (coffee) – Swift - Friday Pie May 21 '21 at 14:01

4 Answers4

3

I am really new to working with code and I dont[sic] quite understand how to work with values in this manner.

OK, I have several tips for you:

① separate your tasks

You ran into a hitch parsing the input in the supplied format, dealing with the comma. Parsing the supplied input files is a totally different problem from the real work, which is summing the columns. Write them separately in the code.

In general you should isolate the "real work" in its own function and have it take parameters as input and returns results as a function return value. The input and output are written separately.

That gives you the added bonus of automating the testing by calling the "work" function with built-in test cases. In this case, it allows you to defer figuring out the parsing. You just pass in test data for now, to get the "work" part working, and then you can come back to parsing the input. Then, when you do need help, it will be specific to "parsing comma separated values" and have nothing to do with why you want them.

② To handle groups of values, you use the array.

This means subscripting or iterating, using loops (or library algorithms) to take what you want to do, written once, and apply it to each value in the array.

Given arrays input and sum, you can accumulate the current row (input) into the running sum with code like this:

for (size_t i = 0;  i < COLS;  ++i) {
    sum[i] += input[i];
}

overall program sketch

open the file
repeat three times:
    read a row of input
    accumulate the sum with the new input
print the results

Note, as explained in the first topic, that read a row and accumulate the sum are separate functions and separate sub-tasks to figure out. This is called top-down decomposition of a problem.

It's best to use parameters for input and return for output of the function, but for this simple task I'll just use a global variable. Passing/returning is probably harder than the task you are learning! Note though that this is unrealistic in that in real code you would not want to use global variables like this. However, you might turn this into an "object", which you'll learn later.

#include <fstream>
constexpr size_t ROWS = 3;
constexpr size_t COLS = 5;

int input[COLS];
int sum[COLS];

std::ifstream infile;

int main()
{
    infile.open("values.txt");
     // todo:  check for success and feedback to the user if failed
    // skipped:  zero out the sum array.  Global variable start at 0,
    //           but more generally, you would need to initialize this.
    for (size_t row= 0;  row < ROWS;  ++row) {
        read_row();
        sum_row();
    }
    print_results();
}

The sum_row function is what you saw earlier.

Note that with top-down decomposition, you can stub out parts that you will work on later. In particular, you can have read_row return hard-coded result at first, or read from a different format, so you can test the overall program. Then, go back and get that part working for real.

Top-Down Decomposition is critical for any kind of programming project.

JDługosz
  • 5,592
  • 3
  • 24
  • 45
1

Oops... most of your code is useless, and what remains is not really good.

Writing good programs is not a matter of adding C++ instructions one after the other. You must first design the overall structure of your program.

Here you have an input file containing lines of 5 comma separated values and want to compute an array (of size 5) containing the sum of the columns.

Let go from a high level

open the file
loop over the lines
    loop 5 times:
        read a field up to a comma (or end of the line)
        decode that field into an int value
        sum that value into an array
close the file

Ok, to be able to sum the values into an array, we will have to define the array before the loop and initialize its elements to 0.

Ok, C++ provide std::ifstream to read a file, std::getline to read a stream up to a delimiter (default being newline), std::istringstream to read the content of a string as an input stream and std::stoi to decode a string representing an int value.

Once this is done, but only after:

  • the program structure is clearly designed
  • the required tools from the standard library have been identified

it is possible to sit down in front of your keyboard and start coding.

BTW, this program will never require the declaration of 15 variables a to o nor an array of 15 integers: only int bins[5]...

It could be (tests omitted for brievety):

int bins[5] = {0};  // initializing the first value is enough, others will be 0
std::ifstream in("values.txt");
std::string line;
while (std::getline(in, line)) {
    // std::cout << line << '\n';  // uncomment for debug
    std::stringstream ss(line);
    for(int& val: bins) {    // directly loop over the bins array
        std::string field;
        std::getline(ss, field, ',');
        val += std::atoi(field.c_str());
    }
}

Of course, for a professional grade (or simply robust) program, every input operation should be followed by a test on the stream...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

You can use the std::getline function within the string library to get each comma separated integer.

std::ifstream myfile("values.txt");

for(int i = 0; i < 15; i++)
{
    std::string integer_as_string;
    std::getline(myfile, integer_as_string, ',');
    calc[i] = std::stoi(integer_as_string);
}

myfile.close();

Here we specify that the getline function will read a line of characters in the input until a , character is found. This string is assigned to the integer_as_string variable which will then be converted to an integer and gets assigned to the array.

Also note that i <= 15 will result in undefined behavior. You can further read it here: Wikipedia. And the myfile.close() function was set inside the for loop. This means that in every iteration, you will be closing the file. This is not needed. I think what your looking for is something like this.

std::ifstream myfile("values.txt");

for(int i = 0; i < 15; i++)
{
    std::string integer_as_string;
    std::getline(myfile, integer_as_string, ',');
    calc[i] = std::stoi(integer_as_string);

    std::cout << calc[i] << std::endl;
}

myfile.close();

a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];

References:
std::stoi
Why is "using namespace std;" considered bad practice?

D-RAJ
  • 3,263
  • 2
  • 6
  • 24
  • 1
    @Scheff Sorry I miss spelled it. Thank you for pointing out! – D-RAJ May 21 '21 at 13:53
  • Using a comma as a delimiter will not stop at a newline character. At the very least, it will be an issue for the last value in each row and when to stop reading the rows. – JDługosz May 21 '21 at 14:28
0

First, your array have element with indices from 0 to 14, thus for(int i = 0; i <= 15; i++) should be for(int i = 0; i < 15; i++)

The loop itself might benefit from error-checking. What if file contains less than 15 values?

for(int i = 0; i <= 15; i++)
{
    // you want check status of myfile here. 
    
}

myfile >> calc[i] wouldn't work well with commas unless you add comma to a separator class for that stream. Albeit that can be done that's a little large change and one can use getline (see answers here for examples) instead to specify separator.

If you want named variables to refer to element of array, you can make them references and structurally bind them to array (or other tuple-like data structure, e.g. struct, etc.) provided you have access to C++17

int calc[15] = {};
auto& [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o] = calc;

a would become a reference to calc[0], b to calc[1] and so on.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • He should not be using `a,b,c` etc. as individual variables. Part of his question is to understand how to work with the arrays. – JDługosz May 21 '21 at 14:24