1

Let's assume that I have the following input:

1 2 3 
5 6
10 11
13

stored in wow.txt.

I would like to create a function that reads each line of the input and produces its sum and save that as sum.txt using C++.

In the input file, we have the following:

1) we don't know the length of each line, but it has at most 10 integers.
2) each integer is separated by a space.

So I started with

ifstream inFile;
inFile.open("wow.txt");
ofstream outFile;
outFile.open("sum.txt");

and wasn't sure what to do next.

Some of my friends recommended me using getline, tokenize each line, and then convert string to integer, but I was wondering if there are easier ways of doing it without having to change the type (int to string, string to int) back and forth.

Any help would be greatly appreciated.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
user98235
  • 830
  • 1
  • 13
  • 31
  • 2
    `easier ways of reading the input line by line` the function's called `getline`, how much easier were you hoping this to be? Just try something and if you get stuck come back. – user657267 Jul 28 '14 at 02:10
  • Do you want the sum of all the numbers or just the ones for each line? – David G Jul 28 '14 at 02:11
  • 0x499602D2 just one for each line. – user98235 Jul 28 '14 at 02:13
  • "some of my friends recommended me using `getline`, tokenize each line, and then convert string to integer" - get some new friends. You should use `getline`, then create an `istringstream` from the line you've read, then have a `while` loop reading numbers from the `istringstream` - far simpler than explicitly "tokenize"-ing the lines or manually doing `string`->`int` conversions. I could write it but then you'd learn nothing. – Tony Delroy Jul 28 '14 at 02:16
  • Yang Unfortunately, I don't have enough reputation to upvote answers... (you need at least 15 reputation to upvote, but I have 14). – user98235 Aug 08 '14 at 00:42
  • Yang i have 19 now. I accepted and upvoted yours. – user98235 Aug 09 '14 at 00:54

2 Answers2

6

Using getline and istringstream:

#include <fstream>
#include <iostream>
#include <sstream>

using namespace std;

int main() {
  ifstream inFile("wow.txt");
  if (!inFile) {
    cerr << "File wow.txt not found." << endl;
    return -1;
  }
  ofstream outFile("sum.txt");

  // Using getline() to read one line at a time.
  string line;
  while (getline(inFile, line)) {
    if (line.empty()) continue;

    // Using istringstream to read the line into integers.
    istringstream iss(line);
    int sum = 0, next = 0;
    while (iss >> next) sum += next;
    outFile << sum << endl;
  }

  inFile.close();
  outFile.close();
  return 0;
}

Output:

6
11
21
13
Yang
  • 7,712
  • 9
  • 48
  • 65
1

I'm not a big fan of the std::getline() and then use std::istringstream approach: streams aren't free to create. At the very least, the inner `std::istringstream should be constructed once and then be reset, even though this requires to clear the flags:

std::istringstream iss;
for (std::string line; std::getline(std::cin, line); ) {
    iss.clear();
    iss.str(line);
    // ...
}

The call to iss.clear() resets the stream's error flags which get set to eventually indicate that there is no more data. With iss.str(line) the string stream's internal data is set.

Instead of creating or setting an std::istringstream I would arrange for the newline to set the input stream to be false, i.e., to have std::ios_base::failbit set. For the advanced approach to do so, I'd change the definition of whitespace for the std:ctype<char> facet in the std::locale used by the stream. However, that's shooting the big guns! For the task at hand, a simple manipulator used before each input can be used to a similar effect:

#include <iostream>
#include <cctype>

using namespace std;

std::istream& skipspace(std::istream& in) {
    while (std::isspace(in.peek())) {
        int c(in.peek());
        in.ignore();
        if (c == '\n') {
            in.setstate(std::ios_base::failbit);
            break;
        }
    }
    return in;
}

int main() {
    int sum(0);
    while (std::cin >> sum) {
        for (int value; std::cin >> skipspace >> value; ) {
            sum += value;
        }
        std::cout << "sum: " << sum << "\n";
        std::cin.clear();
    }
    return 0;
}

Most of the magic is in the manipulator skipspace(): it skips whitespace until either the end of the stream is reached or a newline is consumed. If a newline is consumed, it puts the stream into failure state by setting the flag std::ios_base::failbit.

The loop computing the sum simply reads the first value. If this fails, e.g., because a non-integer is found, the input fails and no further output is generated. Otherwise whitespace is skipped using the skipspace() followed by reading the next value. If either of these fails, the current sum is printed and the stream is cleared for the next sum to be read.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380