问题
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.
回答1:
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
回答2:
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.
来源:https://stackoverflow.com/questions/24987600/i-would-like-to-create-a-function-that-reads-each-line-of-the-input-and-produces