I, at least, would rather see/use an approach based more closely on standard algorithms. The practicality of that can depend heavily upon existing code though. In particular, it makes it much easier to make use of existing components, but those components are often somewhat more difficult to write. For this answer I'm going to use some components I've previously posted. Without them, the practicality of this answer might be open to somewhat more question (when this problem is taken in isolation).
First, reverse_line
can use istream_iterator
s and std::ostream_iterator
s to simplify the logic:
std::string reverse_line(std::string const &input) {
std::istringstream buffer(input);
std::vector<std::string> words{std::istream_iterator<std::string>(buffer),
std::istream_iterator<std::string>() };
std::ostringstream ret;
std::copy(words.rbegin(), words.rend(),
std::ostream_iterator<std::string>(ret, " "));
return ret.str();
}
Note that this appends an extra space at the end of each line. I'd guess that's allowed, but if not you can use an infix_ostream_iterator
in place of the ostream_iterator
above.
Then, in main
we can use std::transform
to do most of the real work. The main difficulty here is that we need to read the input line-by-line, but transform
uses iterators which (as shown in reverse_line
) normally read word-by-word. There are a number of ways to deal with that, many of which are outlined in answers to an old SO question.
For the moment, I'm going to use the line
class from my answer to that question. Using that, the main loop can look like this:
std::transform(std::istream_iterator<line>(infile), std::istream_iterator<line>(),
std::ostream_iterator<std::string>(outfile, "\n"),
reverse_line);
The next point would be to consider the real intent of your code. On one hand, you've hard-coded the names of the input and output files. This pretty much restricts the code to being used in its original setting. If that's your real intent, I'd probably also skip reporting errors when/if you can't open the input and/or output file. I'd just take for granted that opening them will work, and the user's indication of failure is that it either doesn't create an output file at all (if it couldn't open the output file) or creates an empty output file (if it couldn't open the input file). For a contest, this is almost certainly perfectly acceptable behavior.
If, however, you want to be able to put the code to wider use, you almost certainly want to let the user specify names for the input and output files (possibly including defaults, such as reading from cin
and writing to cout
). In this case, you almost certainly want to report errors (at least) a little more comprehensively, such as printing out the actual name of the file you couldn't open when a failure occurs. In particular, for a real tool it's rarely acceptable to just tell the user: "sorry, it didn't work". You need to provide information for the user to put to use in diagnosing and fixing the problem so they can make it work the next time around.
For this scenario, I'd also consider whether you want to check for pre-existence of the output file before opening it for writing (since opening in write mode will destroy whatever data it currently contains.
Oh--one more thing I'd meant to mention above, and almost forgot completely. Right now your code to skip that first line signifying the number of test cases seems unnecessarily complex. Right now you have:
infile>>test_cases; //this is never used
getline(infile,line); //discards first line
An istream has an ignore
member function that's probably preferable to reading an integer; it makes it much more explicit that you're ignoring data from the input stream. Alternatively, you could just read all the data with getline
:
std::string ignore;
getline(infile, ignore);
This makes it fairly apparent that you're reading and ignoring a line from the input stream.