First an aside: you can access command-line arguments by using an
alternate form of the main
function:
int main( int argc, char* argv[] ) { cout << "You supplied " << (argc-1) << " command-line options" << endl; for( int i = 0; i < argc; i++ ) { cout << "argument " << i << " is " << argv[i] << endl; } return 0; }
In the above example, argv
is an array of C-style
strings and argc
is the size of that array.
argv
: the name of your program. All subsequent
command-line arguments will come afterwards. This means that if you
supply two command-line arguments to your program, argv
will have three strings: your program name and the two
arguments.You can use the ifstream
class to read data from a
file:
#include <fstream> using std::ifstream; using std::ofstream; #include <string> using std::string; #include <iostream> using std::cout; using std::endl; int main() { ifstream fin( "myTextFile.txt" ); string s; while( getline(fin,s) ) { // the getline function reads // data from fin into the string // s one line at a time cout << s << endl; } fin.close(); }
Instead of reading the text file line-by-line, you can read it word-by-word:
ifstream fin( "myTextFile.txt" ); string s; while( fin >> s ) { // read one whitespace-delimited // word at a time cout << s << endl; } fin.close();
...or we can read one character at a time:
ifstream fin( "myTextFile.txt" ); char c; while( fin >> c ) { // read one char at a time cout << c << endl; } fin.close();
The following code uses the ifstream
and
ofstream
classes to read text from one file and write to
another, adding line numbers:
int main( int argc, char* argv[] ) { // make sure we have at least two command-line arguments if( argc < 3 ) { cerr << "Usage: " << argv[0] << " <source> <dest>" << endl; return -1; } int lineNum = 0; ifstream fin( argv[1] ); // our input file stream ofstream fout( argv[2] ); // output file stream string s; while( getline(fin,s) ) { // grab input, line-by-line fout << right << setw(5) << setfill('.') << lineNum << ": " << s << endl; lineNum++; } fin.close(); fout.close(); }
Note that in the code above, you can use fin
and
fout
just like you'd use cin
and
cout
, respectively. That's because the classes of these
instances share common parent classes. Check out the diagram on
this useful page to get an idea of how the C++ input and output
streams are organized.
If you want to open a file with a string
filename,
you'll have to use the c_str()
method of the string
class:
string filename = "myFile.txt"; ifstream fin( filename ); // this won't compile! ifstream fin( filename.c_str() ); // use this instead ... fin.close();
This is necessary because the constructor in question is expecting a C-style string, not an instance of the C++ string class.
In the above code, you might have noticed that some rather odd
things get put in the fout
stream. What are
right
, setw(5)
, and
setfill('.')
? They are stream
manipulators, which are used to set properties of the associated
stream.
Consider the following code, which uses stream manipulators to display numbers in decimal, hexadecimal, and octal:
#include <iostream> using std::dec; using std::hex; using std::oct; #include <iomanip> using std::setw; using std::setfill; ... for( int i = 0; i < 30; i++ ) { cout << setfill('.') << setw(10) << dec << i << setw(10) << hex << i << setw(10) << oct << i << endl; }
setfill
)
are sticky, which means that the change they make to the stream will
remain in effect until changed by some other manipulator. Other
manipulators (like hex
) only affect the next thing that's
dumped into the stream.Interestingly enough, we've been using one manipulator for a while
now without even knowing it. The endl
manipulator dumps
a newline into the stream and flushes that stream, ensuring that any
buffered output will be written to the stream immediately.
So far, we've seen streams that interact with the console
(cin
), screen (cout
), and files
(ifstream
and ofstream
). You can also use
streams to read and write to strings, using the
stringstream
classes:
#include <sstream> using std::istringstream; ... // this string contains the data that we want to parse string employeeRecord = "Simpson Homer 7g 36 6333.3 4.3"; // we'll use an istringstream to read data from that string // and into a few variables istringstream iss( employeeRecord ); string firstName, lastName; iss >> lastName; iss >> firstName; string sector; iss >> sector; int age = -1; iss >> age; double salary = -1; iss >> salary; double donutsPerDay = -1; iss >> donutsPerDay; cout << setfill('.'); cout << left << setw(20) << "Name: " << firstName << " " << lastName << endl; cout << left << setw(20) << "Sector: " << sector << endl; cout << left << setw(20) << "Age: " << age << endl; cout << left << setw(20) << "Monthly Salary: " << salary << endl; cout << left << setw(20) << "Donuts/Day: " << donutsPerDay << endl;
There is also a corresponding ostringstream
class that
can be used to build up a string:
#include <sstream> using std::ostringstream; ... // we'll write data to oss, just like a normal stream ostringstream oss; for( int i = 0; i < 30; i++ ) { oss << setfill('.') << setw(10) << dec << i << setw(10) << hex << i << setw(10) << oct << i << endl; } // extract the string from oss with the str() function -- // it contains all of the data that we wrote string s = oss.str(); cout << s; cout << "s.length(): " << s.length() << endl;