C++ Tutorial - fstream: input and output
C++ provides methods of input and output through a mechanism known as streams.
Streams are a flexible and object-oriented approach to I/O. In this chapter, we will see how to use streams for data output and input. We will also learn how to use the stream mechanism to read from various sources and write to various destinations, such as the user console, files, and even strings.
In cout stream, we throw some variables down the stream, and they are written to the user's screen, or console. Streams vary in their direction and their associated source or destination.
For example, the cout stream is an output stream so its direction is out. It writes data to the console so its associated destination is console. There is another standard stream called cin that accepts input from the user. Its direction is in, and its associated source is console.
cout and cin are predefined instances of streams that are defined within the std namespace in C++.
C++ uses type-safe I/O, that is, each I/O operation is executed in a manner sensitive to the data type. If an I/O member function has been defined to handle a particular data type, then that member function is called to handle that data type. If there is no match between the type of the actual data and a function for handling that data type, the compiler reports an error. Therefore, improper data cannot sneak through the system unlike in C, where allowing for some subtle errors.
Users can specify how to perform I/O for objects of user-defined types by overloading the stream insertion operator (<<) and the stream extraction operator (>>). This extensi- bility is one of C++'s most valuable features.
In the code below, the variable x is an integer but a character input is assigned to it. Though the default setting for exceptions() is goodbit, the overloaded exceptions(iostate) function gives us control over how we set the behavior for the wrong input. The line cin.exceptions(ios::failbit) causes failbit to throw exception.
#include <iostream> #include <exception> using namespace std; int main() { int x; // make failbit to throw exception cin.exceptions(ios::failbit); try { cin >> x; cout << "input = " << x << endl; } catch(ios_base::failure &fb;) { cout << "Exception:" << fb.what() << endl; cin.clear(); } return 0; }
Output may be different depending on the implementation:
a Exception:ios_base::failbit set
<fstream> library provides functions for files, and we should simply add #include <fstream> directives at the start of our program.
To open a file, a filestream object should first be created. This is either an ofstream object for writing, or an ifstream object for reading.
The declaration of a filesream object for writing output begins with the ofstream, then a name for that filestream object followed by parentheses specifying the file to write to: ofstream object_name ("file_name");
#include <fstream> #include <string> #include <iostream> using namespace std ; int main() { string theNames = "Edsger Dijkstra: Made advances in algorithms, the semaphore (programming).\n" ; theNames.append( "Donald Knuth: Wrote The Art of Computer Programming and created TeX.\n" ) ; theNames.append( "Leslie Lamport: Formulated algorithms in distributed systems (e.g. the bakery algorithm).\n") ; theNames.append( "Stephen Cook: Formalized the notion of NP-completeness.\n" ) ; ofstream ofs( "theNames.txt" ) ; if( ! ofs ) { cout << "Error opening file for output" << endl ; return -1 ; } ofs << theNames << endl ; ofs.close() ; return 0 ; }
With output as below - theNames.txt:
Edsger Dijkstra: Made advances in algorithms, the semaphore (programming). Donald Knuth: Wrote The Art of Computer Programming and created TeX. Leslie Lamport: Formulated algorithms in distributed systems (e.g. the bakery algorithm). Stephen Cook: Formalized the notion of NP-completeness.
TABLE
ios | Description |
---|---|
ios::out | Open a file to write output |
ios::in | Open a file to read input |
ios::app | Open a file to append at the end |
ios::trunc | Truncate the existing file (default) |
ios::ate | Open a file without truncating, and allow data to be written anywhere in the file. |
ios::binary | Treat the file as binary format rather than ASCII so that the data may be stored in non-ASCII format. |
When a filestream object is created, the parentheses following its name can optionally contain additional arguments. The arguments specify a range of file modes to control the behavior of the filestream object. Since these file modes are part of the ios namespace, they must be explicitly addressed using that prefix as shown the table above.
Several mides may be specified if they are separated by a pipe, "|". To open a file for binary output looks like this:
ofstream object_name ("file_name", ios::out | ios::binary);
The default behavior when no modes are specified considers the file as a text file that will be truncated after writing. One of the most commonly used mode is ios::app, which ensures existing content will be appended when new output is written to the file.
The ifstream object has a get() function that can be used to read a file. In the example below, we read the input file one character at a time:
#include <string> #include <fstream> #include <iostream> using namespace std ; int main() { char letter ; int i ; string line ; ifstream reader( "Shakespeare.txt" ) ; if( ! reader ) { cout << "Error opening input file" << endl ; return -1 ; } for( i = 0; ! reader.eof() ; i++ ) { reader.get( letter ) ; cout << letter ; /* getline( reader , line ) ; cout << line << endl ; */ } reader.close() ; return 0 ; }
Output is:
Macbeth: To-morrow, and to-morrow, and to-morrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing.
Similar to the above example, the following gets words from the Linux dictionary /usr/share/dict/words, and puts into a vector after filtering words that start with lower case letter:
#include <iostream> #include <fstream> #include <string> #include <vector> using namespace std; int main() { string line; vectordictionary; ifstream dict_reader("/usr/share/dict/words"); if( !dict_reader ) { cout << "Error opening input file - dict " << endl ; exit(1) ; } while(!dict_reader.eof()) { getline(dict_reader,line); if(line[0] >= 'a' && line[0] <= 'z') dictionary.push_back(line); } for(int i = 0; i < dictionary.size(); i++) { cout << dictionary[i] << " " << endl; } }
The output is:
a a' a- a. a1 aa aaa ... zythum zyzzyva zyzzyvas zZt
The fstream::eof() may not work under certain conditions, so sometimes we may want the following lines:
// while(!dict_reader.eof()) { // getline(dict_reader,line); => while(getline(dict_reader,line)) {
The getline() can have additional argument for a delimiter where to stop reading. This can be used to read in tabulated data:
Tommy Tutone San Francisco 401-867-5309 Celine Dion Lava Canada 450-978-9555 LA Times Los Engeles 800-252-9141
Each item in the recode above is separated by tab and line feed. The following code reads in the data and prints out each item in a line.
#include <fstream> #include <iostream> #include <string> using namespace std; int main() { const int RECORDS = 12; ifstream reader("PhoneBook.txt"); if(!reader) { cout << "Error: cannot open input file" << endl; return -1; } string item[RECORDS]; int i = 0; while(!reader.eof()) { if((i+1) % 4 == 0) getline(reader,item[i++],'\n'); else getline(reader,item[i++],'\t'); } i = 0; while(i < RECORDS) { cout << "First name " << item[i++] << endl; cout << "Last name " << item[i++] << endl; cout << "Area " << item[i++] << endl; cout << "Phone " << item[i++] << endl << endl; } reader.close(); return 0; }
Output:
First name Tommy Last name Tutone Area San Francisco Phone 401-867-5309 First name Celine Last name Dion Area Lava Canada Phone 450-978-9555 First name LA Last name Times Area Los Engeles Phone 800-252-9141
The following example reads in int data from a file, and then fills in vector. If the space is not enough to hold the data, the vector resizing it by 10.
#include <iostream> #include <fstream> #include <vector> using namespace std; int main() { // vector with 10 elements std::vector<int>v(10); // ifp: input file pointer std::ifstream ifp("data", ios::in); int i = 0; while(!ifp.eof()) { ifp >> v[i++]; if(i % 9 == 0) v.resize(v.size() + 10); } std::vector<int>::iterator it; for(it = v.begin(); it != v.end(); ++it ) std:: cout << *it << ' '; std::cout << endl; }
The input file looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 22 24 25
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 22 24 25 0 0 0 0 0
The following example is almost the same as the previous example: it reads in string data from a file and fills in vector.
The std::istreamstd::ctype_base::space is the default delimiter which makes it stop reading further character from the source when it sees whitespace or newline.
As we can see from the data file (names) we're using:
Mao Asada Carolina Kostner Ashley Wagner Gracie Gold Akiko Suzuki Kanako Murakami Adelina Sotnikova Kaetlyn Osmond Yuna Kim Julia Lipnitskaiaas an input. When we reads in the data, it stores first_name and last name into the vector. But we want to treat them as a pair. So, we later put the pair into a map.
Here is the code:
/* w.cpp */ #include <iostream> #include <fstream> #include <string> #include <vector> #include <map> using namespace std; int read_words(vector<string>& words, ifstream& in) { int i = 0; while(!in.eof()) in >> words[i++]; return i-1; } int main() { ifstream ifp("names"); vector<string> w(500); int number_of_words = read_words(w, ifp); w.resize(number_of_words); for(auto it : w) cout << it << " "; cout << endl; map<string, string> wMap; for(int i = 0; i < number_of_words;) { wMap.insert(pair<string, string>(w[i], w[i+1])); i += 2; } cout << "wMap.size()=" << wMap.size() << endl; for(auto it = wMap.begin(); it != wMap.end(); it++) cout << it->first << " " << it->second << endl; }
Output:
wMap.size()=10 Adelina Sotnikova Akiko Suzuki Ashley Wagner Carolina Kostner Gracie Gold Julia Lipnitskaia Kaetlyn Osmond Kanako Murakami Mao Asada Yuna Kim
Note that we're using C++11 auto keyword that can deduce the type from context, we should let the compiler know we want the file compiled with C++11:
g++ -std=c++11 -o w w.cpp
Here is an example that shows output of 3 integers with field width 5 and left aligned.
// Russian Peasant Multiplication #include <iostream> #include <iomanip> using namespace std; int RussianPeasant(int a, int b) { int x = a, y = b; int val = 0; cout << left << setw(5) << x << left << setw(5) << y << left << setw(5) << val << endl; while (x > 0) { if (x % 2 == 1) val = val + y; y = y << 1; // double x = x >> 1; // half cout << left << setw(5) << x << left << setw(5) << y << left << setw(5) << val << endl; } return val; } int main() { RussianPeasant(238, 13); return 0; }
The output should look like this:
238 13 0 119 26 0 59 52 26 29 104 78 14 208 182 7 416 182 3 832 598 1 1664 1430 0 3328 3094
Actually, the multiplication is known as Russian Peasant Multiplication. Whenever x is odd, the value of y is added to val until x equals to zero, and then it returns 3094.
What would be the output from the code below.
#include<stdio.h> int main() { FILE *fp; char c[1024]; fp = fopen("test.txt", "r"); // "Kernighan and Ritchie" c[0] = getc(fp); // c[0] = K fseek(fp, 0, SEEK_END); // file position moved to the end fseek(fp, -7L, SEEK_CUR); // 7th from the end, with is 'R' of "Ritchie" fgets(c, 6, fp); // read 6-1 characters from 'R' puts(c); // "Ritch" return 0; }
Q: In a file contains the line "The C Programming Language\r\n".
This reads this line into the array s using fgets().
What will s contain?
Ans: "The C Programming Language\r\0"
That's because char *fgets(char *s, int n, FILE *stream) reads characters from stream into the string s.
It stops when it reads either n - 1 characters or a newline character, whichever comes first.
Therefore, the string str contains "The C Programming Language\r\0".
scanf() returns the number of items of the argument list successfully filled. In the code below, though 99 is given, the out is 1 because scanf() returns the number of input which is 1.
#include <stdio.h> int main() { int i; /* 99 is given for the input */ printf("%d\n", scanf("%d", &i;)); /* Though i = 99, print output is 1 */ return 0; }
int main(int argc, char *argv[]){}
- argc will have been set to the count of the number of strings that
- argv will point to, and argv will have been set to point to an array of pointers to the individual strings
- argv[0] will point to the program name string, what ever that is,
- argv[1] will point to the first argument string,
- argv[2] will point to the second argument string, and so on, with
- argv[argc-1] pointing to the last argument string, and
- argv[argc] pointing at a NULL pointer
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization