In class yesterday I made a number of modifications to the mpg.cpp program that was presented in the previous lecture notes. The changes I made will help you with the lab assignment and include:
Also note that I changed the way I handle input in the while loop. The way I did it previously, by putting the read inside the while condition, is a bit sloppy, because file I/O can fail for reasons other than end-of-file. In these other cases, we may want to skip the line of input and continue.
I have now placed a priming read before the while loop and then used the eof method to determine whether the end-of-file has been reached. The priming read initializes the input variables and the file's eof flag. If you are familiar with the phrase "priming the pump", this is the phrase from which priming read is derived. If I fail to put the priming read before the loop, then the loop will get executed once, even if the input file is empty. In that case garbage will get printed because nothing will be read into the input variables.
I have also placed a read statement at the end of the loop to get the next line of input.
#include <iostream>
#include <string>
#include <fstream> -- add fstream to enable file I/O
#include <stdlib.h> -- stdlib.h declares the exit function
using namespace std;
// auto turns out to be a C++ reserved word so I substituted car instead
class car {
public:
string name;
float avg_mpg;
};
// it's better to declare constants rather than hard-coding them
const int NUM_DRIVERS = 3;
/* in order to use command line arguments you must declare the following
two arguments for main:
1) argc: the number of command line arguments. It is equal to 1 plus the
number of command line arguments you provide, because the first
command line argument is actually the name of the executable
2) argv: an array of character strings that contain the command line
arguments. argv[0] is the name of the executable. The command line
arguments provided by the user start at argv[1].
*/
main(int argc, char *argv[]) {
ifstream input_file; -- input_file is an (i)nput (f)ile stream
car max_car; // holds the car with the best gas mileage seen thus far
float avg; // average gas mileage for the current car
int mpg[NUM_DRIVERS]; // gas mileage achieved by drivers for current car
string model; // name of current car
/* make sure that we receive exactly the number of arguments that we
expect. If we receive too few, then argv[1] will be undefined and our
program will crash. If we receive too many, the extra arguments will
be ignored. More importantly, the user probably has a misconception
about how to use our program.
*/
if (argc != 2) {
cerr << "usage: " << argv[0] << " filename" << endl;
/* exit provides us with a way to immediately exit the program and to
provide the user with an error code that indicates why the program
exited abnormally. There are no "standard" values for error codes. They
are defined by the programmer. */
exit(1);
}
/* we must now open the input file and then check to ensure that it was
opened correctly. We can test the stream directly, as I have done here,
or you can call the stream's fail method. My method is the more commonly
accepted idiom. If the open fails, it writes an error message to a
protected part of memory. perror, which stands for (p)rint error, is
a C++ provided function that can read this protected error message and
print it to the console. You can also provide an argument to perror, in
which case perror concatenates your argument to the start of the error
message. Providing an argument to perror can help you locate where in the
source file the error has occurred.
*/
input_file.open(argv[1]);
if (!input_file) {
perror(argv[1]);
exit(1);
}
// initialize max_car.avg_mpg to a small value or else it might never get set
max_car.avg_mpg = -1.0;
/* initial priming read that initializes the input variables and sets
the eof flag to true if the file is empty */
input_file >> model >> mpg[0] >> mpg[1] >> mpg[2];
/* the eof method returns true when end-of-file is reached */
while (input_file.eof() != true) {
// remember to cast either the numerator or divisor to a float in order
// to avoid integer arithmetic
avg = (mpg[0] + mpg[1] + mpg[2]) / (float)NUM_DRIVERS;
if (avg > max_car.avg_mpg) {
max_car.name = model;
max_car.avg_mpg = avg;
}
/* read the next line of input */
input_file >> model >> mpg[0] >> mpg[1] >> mpg[2];
}
cout < "best car = "
< max_car.name
< endl;
cout < "mpg = "
< max_car.avg_mpg
< endl;
}