C++ Tutorial - fstream: input and output
Bookmark and Share
cplusplus logo












Stream

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.



cin & excedption

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


File Writing

<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.


ios

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.




File Reading

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;
  vector dictionary;

  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


Reading in data to fill a vector - A

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


Reading in data to fill a vector - B

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 Lipnitskaia
as 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




Alignment and field width - Russian Peasant Multiplication

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.





Retasting C file IO - fseek

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;
}



Retasting C file IO - fgets

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".




Retasting C file IO - scanf

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;
}



argc/argv
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










Custom Search