I won’t be explaining what flags are. But even if you don’t know about them, reading this chapter to the end may give you some idea, and I believe you will understand the theory. Even so, if you don’t know about the flags in C++, I recommend you to find some reading on this subject.
Okay, let’s start!
The Input/Output system in C++, holds information about the result of every I/O operation. The current status is kept in an object from type io_state, which is an enumerated type (just like open_mode) that has the following values:
godbit | No errors. |
eofbit | End of file has been reached |
failbit | Non-fatal I/O error |
badbit | Fatal I/O error |
The other way to check the I/O status is by using any of the following function:
bool bad();
bool eof(); //remember this one? "Read until the end of the file has been reached!"
bool fail(); //and this one, too… Check if the file opening was successfull
bool good();
The function bad() returns true, if the badbit flag is up. The fail() function returns true if the failbit flag is up. The good() function returns true if there were no errors (the goodbit flag is up). And the eof() function returns true if the end of the file has been reached (the eofbit flag is up).
If an error occurred, you will have to clear it if you want your program to continue properly. To do so, use the clear() function, which takes one parameter. The parameter should be the flag you want to put to be the current status. If you want your program to continue “on clear”, just put ios::goodbit as parameter. But notice that the clear() function can take any flag as parameter. You will see that in the code examples bellow.
I will now show you some example code that will confirm your knowledge.
Example 1: Simple status check
//Replace FileStream with the name of the file stream handle
if(FileStream.rdstate() == ios::eofbit)
cout << "End of file!\n";
if(FileStream.rdstate() == ios::badbit)
cout << "Fatal I/O error!\n";
if(FileStream.rdstate() == ios::failbit)
cout << "Non-fatal I/O error!\n";
if(FileStream.rdstate() == ios::goodbit)
cout << "No errors!\n";
Example 2: The clear() function
#include
void main()
{
ofstream File1("file2.txt"); //create file2.txt
File1.close();
//this bellow, will return error, because I use the ios::noreplace
//open_mode, which returns error if the file already exists.
ofstream Test("file2.txt",ios::noreplace);
//The error that the last line returned is ios::failbit, so let's show it
if(Test.rdstate() == ios::failbit)
cout << "Error...!\n";
Test.clear(ios::goodbit); //set the current status to ios::goodbit
if(Test.rdstate() == ios::goodbit) //check if it was set correctly
cout << "Fine!\n";
Test.clear(ios::eofbit); //set it to ios::eofbit. Useless.
if(Test.rdstate() == ios::eofbit) //and check again if it is this flag indeed
cout << "EOF!\n";
Test.close();
}
Instead using flags, you can use a function that actually does the same- checks if specific flag is up. The functions were mentioned before, so I won’t mention them again. If you are not sure how to use them, just check out the place of the tutorial, where I showed few ways to check if file opening was successful or not. There, I used the fail() function.
Dealing with Binary files
Although, the files with formatted text (all that I talked about so far) are very useful, sometimes you may need to work with unformatted files- binary files. They have the same look as your program itself, and it is much different from what comes after using the << and >> operators. The functions that give you the possibility to write/read unformatted files are get() and put(). To read a byte, you can use get() and to write a byte, use put(). You should remember of get()… I have already used it before. You wonder why even using it, the file looks formatted? Well, it is because I used the << and >> operators, I suppose.
get() and put() both take one parameter- a char variable or character.
If you want to read/write whole blocks of data, then you can use the read() and write() functions. Their prototypes are:
istream &read(char *buf, streamsize num);
ostream &write(const char *buf, streamsize num);
For the read() function, buf should be an array of chars, where the read block of data will be put. For the write() function, buf is an array of chars, where is the data you want to save in the file. For the both functions, num is a number, that defines the amount of data (in symbols) to be read/written.
If you reach the end of the file, before you have read “num” symbols, you can see how many symbols were read by calling the function gcount(). This function returns the number of read symbols for the last unformatted input operation.
And before going to the code examples, I have to add, that if you want to open a file for binary read/write, you should pass ios::binary as an open mode.
Now, let me give you some code examples, so that you can see how stuff works.
Example 1: Using get() and put()
#include
void main()
{
fstream File("test_file.txt",ios::out | ios::in | ios::binary);
char ch;
ch='o';
File.put(ch); //put the content of ch to the file
File.seekg(ios::beg); //go to the beginning of the file
File.get(ch); //read one character
cout << ch << endl; //display it
File.close();
}
Example 2: Using read() and write()
#include
#include
void main()
{
fstream File("test_file.txt",ios::out | ios::in | ios::binary);
char arr[13];
strcpy(arr,”Hello World!”); //put Hello World! into the array
File.write(arr,5); //put the first 5 symbols into the file- “Hello”
File.seekg(ios::beg); //go to the beginning of the file
static char read_array[10]; //I will put the read data, here
File.read(read_array,3); //read the first 3 symbols- “Hel”
cout << read_array << endl; //display them
File.close();
}
Some useful functions
tellg() - Retunrs an int type, that shows the current position of the inside-pointer. This one works only when you read a file. Example:
#include
void main()
{
//if we have "Hello" in test_file.txt
ifstream File("test_file.txt");
char arr[10];
File.read(arr,10);
//this should return 5, as Hello is 5 characters long
cout << File.tellg() << endl;
File.close();
}
tellp() - The same as tellg() but used when we write in a file. To summarize: when we read a file, and we want to get the current position of the inside-pointer, we should use tellg(). When we write in a file, and we want to get the current position of the inside-pointer, we should use tellp(). I won’t show a code example for tellp() as it works absolutely the same way as tellg().
seekp() - Remember seekg()? I used it, when I was reading a file, and I wanted to go to specified position. seekp() is the same, but it is used when you write in a file. For example, if I read a file, and I want to get 3 characters back from the current position, I should call FileHandle.seekg(-3). But if I write in a file, and for example, I want to overwrite the last 5 characters, I have to go back 5 characters. Then, I should use FileHandle.seekp(-5).
ignore() - Used when reading a file. If you want to ignore certain amount of characters, just use this function. In fact, you can use seekg() instead, but the ignore() function has one advantage- you can specify a delimiter rule, where the ignore() function will stop. The prototype is:
istream& ignore( int nCount, delimiter );
Where nCount is the amount of characters to be ignored and delimiter is what its name says. It can be EOF if you want to stop at the end of the file. This way, this function is the same as seekg(). But it can also be ‘\n’ for example, which will stop on the first new line. Here is example:
#include
void main()
{
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
static char arr[10];
//stop on the 6th symbol, if you don’t meet “l”
//in case you meet “l”- stop there
File.ignore(6,’l');
File.read(arr,10);
cout << arr << endl; //it should display "lo World!"
File.close();
}
getline() - I have already mentioned about this one. But there is something I didn’t tell you about. This function can be used to read line-by-line, but it can be set to stop reading if it met a certain symbol. Here is how you should pass the parameters to it:
getline(array,array_size,delim);
And here is a code example:
#include
void main()
{
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
static char arr[10];
/*read, until one of these happens:
1) You have read 10
2) You met the letter “o”
3) There is new line
*/
File.getline(arr,10,’o');
cout << arr << endl; //it should display "Hell"
File.close();
}
peek() - This function will return the next character from an input file stream, but it won’t move the inside-pointer. I hope you remember, that get() for example, returns the next character in the stream, and after that, it moves the inside-pointer, so that the next time you call the get() function, it will return the next character, but not the same one. Well, using peek() will return a character, but it won’t move the cursor. So, if you call the peek() function, two times in succession, it will return a same character. Consider the following code example:
#include
void main()
{
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
char ch;
File.get(ch);
cout << ch << endl; //should display "H"
cout << char(File.peek()) << endl; //should display "e"
cout << char(File.peek()) << endl; //should display "e" again
File.get(ch);
cout << ch << endl; //should display "e" again
File.close();
}
And by the way, I forgot to mention- the peek() function actually returns the ASCII code of the char, but not the char itself. So, if you want to see the character itself, you have to call it the way I did, in the example.
_unlink() - Deletes a file. Include io.h in your program, if you are going to use this function. Here is a code example:
#include
#include
void main()
{
ofstream File;
File.open("delete_test.txt"); //creates the file
File.close();
_unlink("delete_test.txt"); //deletes the file
//tries to open the file, but if it does not exists
//the function will rreturn error ios::failbit
File.open("delete_test.txt",ios::nocreate);
//see if it returned it
if(File.rdstate() == ios::failbit)
cout << "Error...!\n"; //yup,it did
File.close();
}
putback() - This function will return the last read character, and will move the inside-pointer, one with -1 char. In other words, if you use get() to read a char, then use putback(), it will show you the same character, but it will set the inside-pointer with -1 char, so the next time you call get() again, it will again show you the same character. Here is a code example:
#include
void main()
{
//test_file.txt should have this text- "Hello World"
ifstream File("test_file.txt");
char ch;
File.get(ch);
cout << ch << endl; //it will display "H"
File.putback(ch);
cout << ch << endl; //it will again display "H"
File.get(ch);
cout << ch << endl; //it will display "H" again
File.close();
}
flush() - When dealing with the output file stream, the date you save in the file, is not actually immediately saved in it. There is a buffer, where it is kept, and when the buffer gets filled, then the data is put in the real file (on your disk). Then the buffer is emptied, and so on.
But if you want to save the data from the buffer, even if the buffer is still not full, use the flush() function. Just call it this way- FileHandle.flush(). And the data from the buffer will be put in the physical file, and the buffer will be emptied.
And something in addition (advanced)- The flush() function calls the sync() function of the associated streambuf. (The last sentence is from MSDN).
Conslusion
Well, I hope you can now do your File I/O programs! I covered everything I remembered of! I think it is more than enough for most of you! Even so, there are things I didn’t cover… but things I think you will never need. So, if you need even more advanced information on specific topic, search the web. Try google.com for example! But don’t ask me! I won’t answer any e-mails, asking me how to do certain program or else.
If you liked this tutorial, or not, I would be very happy to see your comments.
For more C++ tutorials, visit CPP Home
No comments:
Post a Comment