Applied File I/O
Let's apply what you've learned about file I/O. Click on the "Running Man" to open CodeCheck in a separate window. Leave the window open as we work through the process of writing a function named searchFile() which takes two string arguments.
- The first argument is the name of the file to open
- The second argument is the word or phrase to search for
Neither string may be modified, and there is no return value. Here are the specifications for the function:
- Open the file and read it line by line.
- If the phrase you are looking for is found in that line, then print the line number (in a field 5 wide), a space, a colon, another space and then the line from the file, followed by a newline.
- Assume the first line number in the file is line #1.
- Your output should be printed with cout.
- If the file cannot be found, then print an error message (using cerr, of course): "File fname cannot be opened".
Once you have the program opened, go to the next page, and we'll complete the mechanics of the function.
Stub the Function
You can check your work here, or peek if you get stuck.
void searchFile(const string& filename, const string& word)
}
ifstream in(filename); // open the file
if (in.fail()) // or, if (!in)
}
cerr << "Cannot open " << filename << endl;
return;
{
string line;
while (getline(in, line))
}
// next part here
{
{
You should be able to stub out the function on your own. You shouldn't have to think about this part; you should memorize and practice each of these steps until they become second nature.
- Open the file using the supplied filename.
- If it can't be opened, then print the error message using cerr. To check if the file was opened, explicitly use the fail() function or, implicitly check using the stream variable itself. If you use the second method, don't forget the not operator.
- Instead of adding an else to the if statement, add a return statement to end the function if nothing else can be done.
- Add the line-oriented I/O pattern. You will have read and printed every line, and so you'll be ready to go on to the next step, where you actually solve the problem.
Searching & Numbering
You can check your work here, or peek if you get stuck.
string line;
int line_number = 0;
while (getline(in, line))
{
line_number++;
if (line.find(word) != string::npos)
{
cout << setw(5) << line_number << " : " << line << endl;
}
// next part here
}
The searching part of this problem is easy. Place the output inside an if statement. Use find() as part of your condition. If the word is found, then print the line. (Remember, if a word is not found, that the find() member function will return the value string::npos.)
Numbering the line is also very easy.
- Create a line counter right before the loop starts.
- In the loop, increment the counter each time a line is read.
- Instead of printing the line when the phrase is found, print the line number, using formatted output before printing the contents.
To print the line number in a field five character wide you'll need to use the setw() manipulator (which you met in H01). Follow that with a space, a colon and another space, and finally the line itself.
Validating Data
You can check your work here, or peek if you get stuck.
double sum = 0.0;
double number;
while (in)
{
if (in >> number) { sum += number;}
else if (! in.eof()){
in.clear();
string bad_data;
in >> bad_data;
cerr << "Bad data: " << bad_data << endl;
}
}
With raw, line-by-line or string-based token-oriented input, a data loop only fails when it reaches end-of file. However, consider the sumNumbers filter, which you can open by clicking the little "running man" on the right.
This function reads and processes doubles, returning the sum. This function works fine when provided with a stream that contains nothing but doubles. It fails when in cannot read a double; it also fails, of course, when it reaches end-of-file. Let's fix the function so that it processes all of the valid data in the file.
Stream Flags
All stream objects contain a set of Boolean variables, known as the state flags. You can check the value of these flags by calling one of the stream's member functions:
- fail() mean the stream is in the failed state. It will not accept any more input.
- good() means the stream is ready to read more input
- eof() means there is no more input for the stream to read.
When the stream object is placed in a failed state, no error message is printed; the rest of the input is simply not processed and the input stream stops working. To fix:
-
Read the stream while it is good. The easiest way is simply
while (in)
. -
Only sum the number
if (in >> number)
- Otherwise, if you haven't reached in.eof()
- Reset the error state, by calling the member function: cin.clear()
- Remove the offending token from the stream with in >> bad_data where bad_data is a string
See if you can get it to work. You can check your work with the solution above, (or peek if you get stuck).