File management¶
Let’s compare file management in Python and C++. Below you can see a simple program (written in both languages) that counts the sum of integers in a file. Each of the numbers should be on their own line in the file. There must be no empty lines.
def main():
filename = input("Input file name: ")
try:
file_object = open(filename, "r")
sum = 0
for line in file_object:
sum += int(line)
file_object.close()
print("Sum: ", sum)
except IOError:
print("Error: failed opening the file.")
except ValueError:
print("Error: bad line in the file.")
main()
#include <iostream>
#include <fstream> // Notice the required library for file handling
#include <string>
using namespace std;
int main() {
string filename = "";
cout << "Input file name: ";
getline(cin, filename);
ifstream file_object(filename);
if ( not file_object ) {
cout << "Error: failed opening the file." << endl;
} else {
int sum = 0;
string line;
while ( getline(file_object, line) ) {
sum += stoi(line);
}
file_object.close();
cout << "Sum: " << sum << endl;
}
}
The programs do not, in fact, work identically, because the C++ program
uses the function stoi
to change a line read from the file into an integer,
and the function does not notice if there is additional characters
after the numerical ones.
If there is something at the beginning of the line that does not qualify as
an integer, stoi
will exit with an exception, and the program terminates.
We will get back to the exceptions in C++ at a later stage
(not until the next programming course).
If you wish to read files in C++, you follow the steps below:
- At the beginning of the program, include the library
fstream
. It includes data types used in file processing. - Depending on whether you want to read the file or write in it,
you define an object of either the type
ifstream
orofstream
, and you pass to its constructor the name of the file as a parameter. - A failure in opening a file can be recognized by using
the file variable as the condition of the
if
statement: it will be evaluated astrue
if opening the file was successful, and if not,false
. - Just as in Python, the easiest way to read a file is to handle the reading in a loop, one line at a time, into a string variable, and then handle that string with string operations.
- After you have finished reading from or writing in a file, it is good
practice to close it by calling the method
close
. For example, doing this when writing in files ensures that all written data will be saved onto the hard drive. Also, the methodclose
releases the file from the use of the program.
You can read data from the file variable ifstream
with the input operator
>>
, the same way you are used to reading from the stream
variable cin
.
You write into a file (which means storing onto the hard drive) when you
target the output operator <<
at the ofstream
type file variable,
and this action is similar to what you are used to doing with cout
.
Therefore, stream is a general name to a variable that can be used for handling the peripheral devices of the computer (reading and writing). All the actions with the peripheral devices have been implemented in C++ with data streams. In addition, the mechanism is sophisticated enough that you can handle all the input streams in an identical way. It is the same with output streams.
In practice, it means that both cin
and all the ifstream
type
stream variables are handled with the same operations, and they act
in a similar way to each other.
On the other hand, also cout
and the ofstream
type streams
are handled the same way.
This is good, because then the programmer only needs to learn one mechanism,
and they can use it for several purposes.
Some output and input operations¶
A short list of useful stream handling operations:
cout << output
output_stream << output
You can print or save data into the output stream using the output operator
<<
.cin >> variable
input_stream >> variable
You can read a data element of a known type from the input stream directly into a variable of the same type with the input operator
>>
.getline(input_stream, line)
getline(input_stream, line, separator
)Read one line of text in the input stream and save it into the string variable line. If the call includes a third parameter - a separator of the type
char
- reading and saving into the variable line will be continued until the first separator comes up from the stream.string text_line = ""; // One line of text from the keyboard getline(cin, text_line); // Until the next colon // (might read several lines at a time) getline(file_object, text_line, ':');
input_stream.get(ch)
Read one character (
char
) from the input stream into the variable ch:// You can read the file one character at a time: char input_char; while ( file_object.get(input_char) ) { cout << "Character: " << input_char << endl; }
Checking the success of stream operations¶
A stream variable can be used in C++ as a condition of the if
structure.
The compiler interprets the stream as bool
type (executes
an implicit type conversion), and sets the value as true
if the stream is
valid, and as false
if it is not. For example:
ifstream file_object("file.txt");
if (file_object) {
// Opening was successful, next we will do something to the file
} else {
// Opening was unsuccessful, the stream is invalid
cout << "Error! ..." << endl;
}
You can see the successfulness of all the stream-targeted operations in C++
by writing the operations as the condition of an if
or while
structure.
For example:
while(getline(file_object, line)){
// You will enter the loop structure if reading a line from the stream succeeded
}
// The loop structure ends when you cannot read any more lines,
// i.e. when the whole file has been read
Here is a more detailed explanation of what just happened.
Every time you execute an operation targeting a stream in your program,
the return value of the operation will be the same target stream.
If your operation is included as a condition of an if
or while
structure, the compiler will perform an implicit type conversion for the
stream into a bool
type.
As we explained above, the value will be true
or false
depending on
whether the last operation targeted at that stream was successful.
You will get the simplest solutions to the assignments of this course
by always writing the stream-targeted operations as conditions of the
aforementioned structures
(if
, while
) , because then you simultaneously test
the success of your stream operation.
Trivia: the stream also has the method input_stream.eof()
,
and you can use it to check if the latest attempt to read from the stream
was unsuccessful because the file has been read through.
// First, you need to try to read something from the stream
file_object.get(ch);
// After doing that, you can try to examine
// if the reading attempt was a failure because of the fact
// that there was nothing left to be read.
if ( file_object.eof() ) {
// The file has been read from start to finish: the character
// in the variable is now an undefined and useless value.
} else {
// The variable holds a char type value,
// successfully read from the file.
}
You often see incorrect use of the method eof
.
For example, the following solutions are almost always incorrect:
while ( not file_object.eof() ) { // ERROR!
getline(file_object, line);
...
}
The mistake is that reading the file is set as a success when
the last line has been read. It means that eof
returns false
.
(If you do not understand, look above to see what eof
does again.)
This is the reason why the condition of while
will be true
after
reading the last line, and the program will execute the loop structure
once more, even though the file does not contain any lines to read.
For example, you can use the method eof
to examine whether your
loop structure finished handling a file because it reached the end of the file,
or because of an error.
On this course, we will not practice the special error situations arising
from file management in as detailed a way as here.