Today's lecture concerns remembering C++ input/output. Cin and cout are the ways to read from standard input and write to standard output respectively. They are C++ objects which have methods associated with them. Some of the pertinent ones are cin.eof(), cin.fail() and cin.clear(). For a nice description of these, take a look at my lecture notes from CS102 on the topic.
As a basic program, take a look at rdoubles.cpp:
#include <stdio.h> #include <iostream> #include <string> using namespace std; int main() { double d; int dnum; string s; dnum = 1; while(1) { cout << "Enter a double: "; cout.flush(); cin >> d; if (cin.eof()) exit(0); if (cin.fail()) { cout << "Not a double." << endl; cin.clear(); cin >> s; } else { cout << "Double number " << dnum << " is " << d << endl; dnum++; } } } |
This program reads words on standard input, and if they are doubles, prints them out. When it sees a non-double, it marks that it is not a double. It exits on EOF:
UNIX> g++ rdoubles.cpp -o rdoubles UNIX> rdoubles Enter a double: 1 Double number 1 is 1 Enter a double: 2 Double number 2 is 2 Enter a double: 3 Double number 3 is 3 Enter a double: 4 Double number 4 is 4 Enter a double: 5 6 7 8 Double number 5 is 5 Enter a double: Double number 6 is 6 Enter a double: Double number 7 is 7 Enter a double: Double number 8 is 8 Enter a double: Fred Not a double. Enter a double: 9.0 Double number 9 is 9 Enter a double: 10.33333 Double number 10 is 10.3333 Enter a double: <CNTL-D> UNIX>You'll note that it reads words, and not lines, so that when 5 6 7 8 is entered, the next three cin >> d commands return instantly.
Contrast that with the program below (rdub2.cpp), which uses getline() to read a line, and then sscanf() to convert that line into a double. Thus, if there are multiple words on a line, it checks the first but not the rest.
#include <stdio.h> #include <iostream> #include <string> using namespace std; int main() { double d; int line; string s; line = 0; while(1) { cout << "Enter a double: "; cout.flush(); getline(cin, s); line++; if (cin.eof()) exit(0); if (sscanf(s.c_str(), "%lf", &d) != 1) { cout << "Line " << line << " Not a double." << endl; } else { cout << "Double on line " << line << " is " << d << endl; } } } |
UNIX> g++ rdub2.cpp -o rdub2 UNIX> rdub2 Enter a double: 44.44 Double on line 1 is 44.44 Enter a double: 1 2 3 4 5 Double on line 2 is 1 Enter a double: Fred Line 3 Not a double. Enter a double: Fred 0 2 3 4 Line 4 Not a double. Enter a double: Line 5 Not a double. Enter a double: -55.55 Double on line 6 is -55.55 Enter a double: <CNTL-D> UNIX>You may also use a string-stream to read from strings. I won't go over this in class, but if you're interested, see the basic IO tutorial material mentioned above.
#include <stdio.h> #include <iostream> #include <string> #include "rd.h" using namespace std; double read_double() { double d; string s; while (1) { cin >> d; if (cin.eof()) exit(0); if (cin.fail()) { cin.clear(); cin >> s; } else { return d; } } } |
So that another program may use read_double(), we put a "prototype" or definition of the function into rd.h:
double read_double(); |
Finally, the program userd.cpp uses read_double() to read doubles:
#include <stdio.h> #include <iostream> #include <string> #include "rd.h" using namespace std; main() { while (1) { cout << read_double() << endl; } } |
When you compile this, you can either specify everything on the command line:
UNIX> g++ -o userd userd.cpp rd.cpp UNIX> userd 4 5 4 5 6 Fred 7 6 7 <CNTL-D> UNIX>Or compile each cpp file separately into an object file, and then link each object file to make the executable:
UNIX> g++ -c userd.cpp UNIX> g++ -c rd.cpp UNIX> g++ -o userd userd.o rd.o UNIX> userd 1 2 3 4 1 2 3 4 <CNTL-D> UNIX>Linking from object files is faster than compiling the source files.
UNIX> make clean rm -f *.o rdoubles rdub2 userd UNIX>Now, if I type make, it will compile all the programs by first creating object files and then linking them:
UNIX> make g++ -c -o rdoubles.o rdoubles.cpp g++ -o rdoubles rdoubles.o g++ -c -o rdub2.o rdub2.cpp g++ -o rdub2 rdub2.o g++ -c -o userd.o userd.cpp g++ -c -o rd.o rd.cpp g++ -o userd userd.o rd.o UNIX>Now, if I touch userd.cpp and type make again, it knows to re-make userd.o and userd, but that it doesn't have to do anything to rd.o since rd.cpp was not modified:
UNIX> touch userd.cpp UNIX> make g++ -c -o userd.o userd.cpp g++ -o userd userd.o rd.o UNIX>