An input file stream (ifstream) is a special kind of input stream (istream). This means all operations we can perform on input streams can also be performed on input file streams. The main trick is knowing that when an input file stream fails for any reason, the stream itself turns into 0.
We can construct an input file stream from a file name:
ifstream ifs("data.txt");
If the file isn't found or if its permission level doesn't allow reading, then ifs will enter the fail state and it is set equal to 0:
if (!ifs)
{
cerr << "Can't open
file\n";
return 1;
}
We can use the zero test again to determine when the end of the file is reached. This is more reliable than the eof() function, because that returns false even when only white space characters remain in the file:
while(ifs >> val) { ... }
For example, the following program computes the average of a file of numbers:
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
double num, total = 0;
int count = 0;
string source;
cout << "Enter file name:
";
cin >> source;
ifstream ifs(source.c_str()); // won't
work with C++ strings!
if (!ifs)
{
cerr << "can't open
file";
return 1;
}
while (ifs >> num)
{
total += num;
count++;
}
cout << "Average = " << total/count
<< '\n';
return 0;
}
Of course if a non number is encountered in the file, even so much as a punctuation mark, the loop will exit and return the average of the numbers seen.
An output file stream (ofstream) is a special kind of output stream (ostream). This means any operations we can perform on an output stream can also be performed on an output file stream.
Everyone's favorite file function changes lower case letters to upper case letters. I'm using the toupper() function defined in the standard C library, so I need to include <cctype>:
int main()
{
char c;
string source, dest;
cout << "Enter input file
name: ";
cin >> source;
ifstream ifs(source.c_str());
if (!ifs)
{
cerr << "can't open
file";
return 1;
}
cout << "Enter output file
name: ";
cin >> dest;
ofstream ofs(dest.c_str());
if (!ofs)
{
cerr << "can't open
file";
return 1;
}
while (ifs.get(c))
ofs.put(toupper(c));
return 0;
}
Counting words and lines in a file is harder than you'd think. I increment my word counter each time I encounter a non letter following a letter. If the last character in the file was a letter, I increment my word counter again. I increment my line counter each time I encounter a newline character. If the last character in the file was not a newline, I increment my line count once more. Can you think of other improvements?
int main()
{
char c;
int charCount = 0, wordCount = 0,
lineCount = 0;
bool wasLetter = false;
string source;
cout << "Enter file name:
";
cin >> source;
ifstream ifs(source.c_str());
if (!ifs)
{
cerr << "can't open
file";
return 1;
}
//if (ifs)
//{
while (ifs.get(c))
{
charCount++;
if (isalpha(c))
wasLetter = true;
else
{
if (c == '\n') lineCount++;
if (wasLetter) wordCount++;
wasLetter = false;
}
}
if (wasLetter) wordCount++;
if (c != '\n') lineCount++;
cout << "char count =
" << charCount << '\n';
cout << "word count =
" << wordCount << '\n';
cout << "line count =
" << lineCount << '\n';
/*
}
else
{
cerr << "can't open
file\n";
return 1;
}
*/
return 0;
}
Grep is a famous UNIX utility that searches a given file line-by-line for a given pattern. It prints lines, with their line numbers, that contain the pattern. The ultimate pattern matching algorithm is the subject of an upper division algorithms course. I simply use the find() function encapsulated by string objects. Note that command line arguments are used to get the pattern and file name. This function also uses the standard library's getline() function to fetch an entire line and put it into a string variable:
int main(int argc, char** argv)
{
if (argc != 3)
{
cerr << "Usage: "
<< argv[0] << " PATTERN FILE\n";
return 1;
}
int lineNum = 0;
string line;
string pattern = argv[1];
string file = argv[2];
ifstream ifs(file.c_str());
if (ifs)
while (getline(ifs, line))
{
lineNum++;
if (string::npos !=
line.find(pattern))
cout << lineNum <<
":\t" << line << '\n';
}
else
{
cerr <<"can't open
file\n";
return 1;
}
return 0;
}
Notice that all of these functions depend on the fact that get(), getline(), and >> return the stream as a value after each extraction.