1.11M Members

fstream Tutorial

 
4
 
File I/O
With C++ Fstream

Intro

File handling is as simple as writing in a book, much easier to modify and
find. It's so simple people get confused with it :-). Welcome to the world of file handling.

We will use the c++ fstream classes to do our file handling. So, what is a file? A file is just a bunch of bytes stored on a hardisk. Some have a specific structure others dont. Files are used to save info so that it can be retrived later for use. [I dont think you will want to save 100 people's address in memory will you].

Types of Files

Actually there are only two. Text files and binary files. In text files data is stored as readable chars and binary file are in machine language. So if you output abc123 to a text file you will see abc123 but in a binary file you may see only a bunch of black blocks if you use notepad. The binary files are smaller in size.

Fstream.h

fstream.h provides simultaneous input and output through ifstream, ofstream and fstream.

ifstream       - open the file for input
ofstream       - open the file for output
fstream       - open the file for input/output/both

Writing to a file

Relatively very simple.

Steps:

  • Declare an ofstream var.
  • Open a file with it.
  • Write to the file (there are a couple of ways.)
  • Close it.

Eg Program 1.1

#include <fstream.h>

void main
{
    ofstream file;

    file.open("file.txt");      //open a file

    file<<"Hello file\n"<<75;       //write to it

    file.close();               //close it
}

Methods for Writing to a file

The fstream class is derived from the iostream classes, so you can use ofstream variables exactly how you use cout. So you can use the insertion (<<) operator and the put().

Usages:

    file<<"string\n";
    file.put('c');

Reading from a file

Almost the same.

  • Declare an ifstream var.
  • Open a file with it.
  • Read from the file(there are a couple of ways.)
  • Close it.

Eg Program 1.2

#include <fstream.h>

void main
{
    ifstream file;
    char output[100];
    int x;

    file.open("file.txt");  //open a file

    file>>output;       //write to it
    cout<<output;     //result = Hello file
    file>>x;            
    cout<<x;          //result = 75

    file.close();           //close it
}

Methods for Reading a file

The fstream class is derived from the iostream classes, so you can use fstream variables how you use cin.So you can use the extraction (<<) operator and the put().

Usages:

    file>>char *;       //ie an array
    file>>char;         //single char
    file.get(char);     //single char
    file.get(char *,int);   //read a string 
    file.getline(char *,int sz);
    file.getline(char *,int sz,char eol);

**Notes:***

  1. The file handles can be used like:

    ofstream file("fl.txt");
    ifstream file("fl.txt");

You will use the constructor for opening a file.I would not recommend using this not because it works less well or anything but in most cases it will improve code clarity and prevent errors when you are handling multiple files. If a file handle is used more than once without calling a close() in between them there will be errors. This is just provided for info sake if you need to use it in a hurry or in something small.

  1. Never ever declare a fstream variable globally. It is a bad habit. If you forget to close it next time you run the program it will show access errors to the C: drive (or which ever drive you use) and you will have to restart the computer. Declare them within funtions or classes and close them when their use is over.

  2. If you are doing databases or any file handling for that matter never put any file i/o into classes. It will simply complicate debugging and you may also open a single file multiple times with difffrent objects at the same time. This is definitely not what we want. Classes are only to be used when you have to minimal file i/o but still I recommend you use normal funtions.

  3. ifstream stands for input stream and can only be used for input.

  4. ofstream stands for output stream and can only be used for output.

  5. Any variable declared with ifstream,ofstream or fstream is called a file handle.

That wraps up the simple very stuff. You will not use them much unless you are working with text files or in a small project. Now we will move on to fstream which is more flexible and will be most used. It's easy if you look at it logically.

Dynamic file access

The file streams we discussed have a limitation, they can only do input or output at a time. fstream provides us with a way to read, write randomly without having to close and reopen the file. It has great flexibility involving a bit more work with returns being ten fold. Hey, you cant have everything for free (what fun would it be if everything was handed to you).

Lets look at how a file can be opened with fstream.

Program Example 1.3

void main()
{
    fstream file;

    file.open("file.ext",iso::in|ios::out)

    //do an input or output here

    file.close();
}

Notice anything new? The ios::--- are attributes which define how a file should be opened.

            List of Attributes

ios::in         open file for reading
ios::out        open file for writing
ios::app        open for writing,add to end of file(append).
ios::binary     Binary file
ios::nocreate   do not create the file,open only if it exists
ios::noreplace  open and create a new file if the specified file does not exist
ios::trunc      open a file and empty it.(Boom, all the data is gone,if any)
ios::ate        goes to end of file instead of the begining

Notes

  • The default mode is text mode when a file is opened

  • By default if a file does not exist,a new one is created

  • Multiple attributes can be specified at a time seperated by ...|...|...

  • All attributes start with an ios:: (as if you dint notice it,but still let me hammer it)

  • These attributes can be used with ifstream and ofstream but of course ios::in wont work with ofstream and similarly ios::out wont .............

  • File byte location start from zero (like in arrays)

Now we know how to open a file with fstream. Cool, now come read and write.

Reading and Writing with fstream

The two funtion are exactly similar in usage and simple to understand

file.write(char *,int);     //for writing
file.read(char *,int);      //for reading (gee...:D)

Until now we have only been able to use strings and ints to write/read. Most databases will want to store data in structures or classes. If we had to write a seperate function to split and write each member, brrr horrors. C++ goes to be very nice and provides us with a way to write entire classes or structures with little work

struct x
{
    int i;
    char a;
    char s[10];
}data;

file.write((char*)&data,sizeof(x));
file.read((char*)&data,sizeof(x));

Hold on, what the heck is the char * thingy???.

That's called typecasting. Quite an interesting subject actually. It means converting one data type to another. Here it is converting struct x into a char pointer and it's address is passed to the funtion. If anybody wants to know more, well tell!

The rest is simple. You pass the size of the structure which can be found out by using sizeof();

Eg:

cout<<"\nInt:"<<sizeof(int);
cout<<"\nFloat:"<<sizeof(float);
cout<<"\nChar:"<<sizeof(char);

Now instead of going more yack yack I show you an example which should explain a lot more. Sigh "A picture is worth a thousand word, source is worth a million bytes"

Example Program 1.4

#include <fstream.h>

struct  contact
{
    char name[10];
    int age;
}

struct address
{
    char city[10];
    char country[10];
}

class DtbRec
{
    private:
    contact cnt;
    address adr;

    public:
    void getdata();
    void dispdata();
};

/*
   I will give you a bit of work, make the funtions getdata() and dispdata()
   It's easy and not worth for me to bother with now :-}
*/

void DtbRec::getdata() //get user input
{

}

void DtbRec::dispdata()  //display to screen
{

}

//This program is not tested, have fun fixing errors, if any
//I am a taos programmer so dont expect anything major
//Typing mistakes are not errors
//This was done off the cuff and not even compiled once

void main()
{
    DtbRec xRec;        //temp rec
    fstream fl_h;   //file handle
    char ch;
    int num;

    fl_h.open("database.dtb",ios::in|ios::out|ios::binary);

    do
    {
        cout<<"\n\nFstream Dtb\n"
              <<"\n1.Add records"
              <<"\n2.View records"
              <<"\n3.Modify records"
              <<"\n4.Exit"

              <<"\n\tEnter Choice:";

        cin>>ch;

        if(ch == '1')   //we are dealing with chars not ints
        {
            //Adding a rec

            fl_h.seekp(0,ios::end); //will disscuss this later,this sets the file write pointer to
                            //the end of a file.
            xRec.getdata();     //Get some data from the user

            fl_h.write((char*)&xRec,sizeof(DtbRec);

        }
        else if(ch == '2')
        {
            //View recs

            fl_h.seekg(0,ios::beg); //will disscuss this later,this sets the file read pointer to the 
                            //begining of a file.
            num = 0;
            while(!fl_h.eof())      //will disscuss this later,it check if the file's end has been reached
            {
                n++;
                fl_h.read((char*)&xRec,sizeof(DtbRec);

                cout<<"\nRecord No["<<num<<"]\n";
                xRec.dispdata();    //Show the user all the data present
            }
        }
        else if(ch == '3')
        {
            //Modify me colors ;-)

            cout<<"Enter the record no(starts at 0):";
            cin>>num;

            fl_h.seekg(num*sizeof(DtbRec),ios::beg);    //move the read pointer to where the rec is
            fl_h.read((char*)&xRec,sizeof(DtbRec);  //read it
            xRec.dispdata();                    //Show the info
            xRec.getdata();                 //Let the user change the info

            fl_h.seekp(num*sizeof(DtbRec),ios::beg);    //move the write ponter this time
            fl_h.write((char*)&xRec,sizeof(DtbRec); //overwrite with new info

            //yahoo,modification done.I have seen too many people who just
            //cant get modification .It's so simiple I just cant get why they just cant
            //get it ;-)
        }
    }
    while(ch != '4');

    fl_h.close();       //close the file
    cout<<"\nEnd of Program";
}

Got more doubts now than what was cleared? Good, doubts are the first step towards knowledge. (If you happen to be of an opinion that a teacher should explain everything, sorry it's just not my style. I want thinking students not tape recorders)

Note:

  • Records start at byte zero. I said this once and I will say it again.(Dont forget user friendlyness though.)

  • read() and write() can be used to write both classes and structures to the file in the same way.

  • Always know where you are before you start reading or writing to a file or move the pointer to the area of work.

  • The read pointer and write pointer are separate. So move the correct pointer if you want fstream to work.

  • Always close the file when it's use is over.

  • Always perform checks when necessary or appropriate. fstream has a number of buit-in ones for you :-)

  • Never open a new file with a file handle without calling close().

  • Remember, with structs and classes their size is always fixed so finding the exact location of a record can be done mathematically.

** ------------------------- PART II -------------------------**

Random File Access

With fstream we work with 2 file pointers, read and write. By moving these pointers we go access any part of the file at random. See below:

Reading
1.seekg();  //move the read pointer in bytes
2.tellg();  //returns where the read pointer is in bytes

Writing
1.seekp();  //move the write pointer in bytes
2.tellp();  //returns where the write pointer is in bytes

Both tellp() and tellg() don't take any parameters but return where the pointer location in bytes.

Eg.
    int rp = file.tellg();
    int wp = file.tellp();

The seek pointers take 2 parameters

Eg.

file.seekg(0,ios::beg);

List of seek attributes

ios::beg        move from the beginning of the file
ios::end        move from the end of the file
ios::cur        move from current location of the file

Note:

  • ios::beg is default. I recommend you still pass it where ever you use it.

  • Negative numbers can be used with ios::cur & ios::end.(not ios::beg figure it out)

Part II
Databases & Security
(Based on a true story of a database)

Intro

This is another area people find difficulty. I figured it out myself without any help what so ever. I also learned file handling a year before my classmates by looking at an elder's text which contained only vague descriptions so I guess my eagerness to learn had something
to do with it. I have always loved computers and programming. I fell in love with QBasic the moment I saw it. C++ stole my heart then. Ah, programming is a part of my soul.

And even when I got my own text book nothing what so ever was mentioned about random file access except a short and vague description about the 2 functions and the options. There was no reference what so ever about locating individual records. I can't guess why! Reminds me of a saying "when old, one forgets how it is to be young". That maybe a cause :D.

[oh, this reminds me, almost every comp guy/gal I know works late at night. I do too when I don't have much time in the mornings. We should stop this. Never work in the dark without enough light, nor stay up too late. If your eyes feel slightly sleepy,SLEEP. No more than 1:30 AM at the most, I am thankful I been able to do most of this]

Locating Individual Records

Problem area. Let start at the beginnings. Ever seen a graph paper ?.It has a lot of tiny, small, medium and large sized squares. Also each is being made from the tiniest square. What that got a do with files? Well when you use databases you will almost always use structures and they are always the same size (in that particular database). That means like in the program in Part I all the records are with that one class and when you write it to a file it will still be the same size even if you filled it or not. It's like your water bottle. Whether it's full, half or even empty it will always
take up the same space in your bag. Get it?

How do you find the size of any data type ?Easy.

Size_Of_Data_Type = sizeof(Data_Type); //sizeof is a reserved keyword in C++.

Ok, how will that help us find a record? Well all records are of the same size, they start at zero. They can be thought of as an array too and the concept is similar to how you use normal pointers.

Eg. A struct is 20 bytes in size. The first rec starts at byte zero, the second at byte 20, the next at byte 40 ...... and so on

Eg. Program 2.1

file.seekg( Rec_no * sizeof(Data_Type),ios::beg);
file.read((char*)&rec_var,sizeof(Data_Type));

file.seekp( Rec_no * sizeof(Data_Type),ios::beg);
file.write((char*)&rec_var,sizeof(Data_Type));

Simple as that.

Note:

  • This can be use with ios::beg,ios::cur and ios::end

  • When read() or write() is called the appropriate pointer is moved automatically to the next record or rather by a number of bytes as passed to it(that's the sizeof(Data_Type))

I think now you get random access. How about finding the total number of records in a database?

Eg. Program 2.2

file.seekg(0,ios::end);     //move to the end of the file

int fl_sz = file.tellg();       //tell me where you are, partner( pointer ) !
int total_no_rec = fl_sz/sizeof(Data_Type);

file.seekg(0,ios::beg);     //move to the beg of the file

cout<<"There are "<<total_no_rec<<" (s) in this database ";

We divide the total file size by the size of the structure to find the number of record. Simple maths eh?

That's it you have full knowledge of how to handle fstream. Now all you need is creativity, so use it. I won't tell you all, so think. Knowledge can be gained from others but wisdom only from yourself (and from God).

Ah, I love this. Here is some work for you.

  • Re-write the example program to include random reading, writing, and modification on multiple databases. [Put in error checks and don't allow the user to read or modify a rec which does not exist]
  • Find a way to write a whole single dimensional array of a struct to a file with just one write statement. [No hints, re-read this paper if you have to]
  • Deletion. Add a way to allow a person to delete an existing record.
  • [One way to do it, copy all the records except the one to be deleted to another temp file, open the database with ios::trunc and copy the records from the other temp file.]

Error Checking

These are some funtions that help to keep track of errors in your database if any

Error Funtions

good()      returns true if the file has been opened without problems
bad()           returns true if there were errors when opening the file
eof()           returns true if the end of the file has been reached

All are member funtions and are used as file_handle.errror_funtion()

Eg.

char ch;
ifstream file("kool.cpp",ios::in|ios::out);

if(file.good()) cout<<"The file has been opened without problems;
else cout<<"An Error has happend on opening the file; 

while(!file.eof())
{
    file>>ch;
    cout<<ch;
}

You should have understood what those funtions are for. Implement them when you do you file handling. good() should be called after opening a file to see it has been opened properly. bad() is the opposite of good().

Encryption

Aha, any decent database program must encrypt its files. Just open up any of the files from the example programs with notepad and you will be able to see the text. We definitely don't want people to see that.

With encryption you can scramble the text so people can read it. Now there are a lot of encryption schemes and a lot of other methods for protecting data. I will just show you a couple of methods.

Binary Shift

The simplest of all. In this you increase or decrease a char by a number.

void b_shif(char *s,int n)
{
    for(int i=0;s[i]!=0;i++)
    {
        s[i] += n;
    }
}

XOR

Another type of encryption. Exclusive-OR encryption, is almost unbreakable through brute force methods. It is susceptible to patterns, but this weakness can be avoided through first compressing
the file (so as to remove patterns). This encryption while extremely simple, is nearly unbreakable.

int xor(char *string, char *key)
{
    int l=0;
    for(int x=0; string[x]!='\0'; x++)
    {       
        string[x]=string[x]^key[l];
        l++;
        if(l>=strlen(key)) l=0;
    }
    return 0;
}

Encrypt an entire structure

void struct_enc_xor(char *str,char *key,int sz)
{
    int l=0;
    for(int x=0; str[x]!='\0'; x++)
    {       
        str[x]=str[x]^key[l];
        l++;
        if(l>=strlen(key)) l=0;
    }
    return 0;
}

Use it exactly how you used read().

Eg.

struct_enc_xor((char*)&cRec,"password",sizeof(DtbRec));

Exit

Encryption is a very interesting topic. A lot of research has gone into it. If you are interested there are lot of pages on the web which describe encryption and various schemes. Then there is stenography,
the art of hiding info in any file like text within a bitmap or an mp3. Have fun researching.

 
0
 

Cool. In addition, DaniWeb users can always use the Thread Tools dropdown and then select Printable Version for any thread :)

 
1
 

Hello!
I have a little question. Lets say, I have a file called word.txt and it contains 100 over words.
How do I add a new word and SAVING it so I can use this word from this file in future?

Thanks

 
1
 

Quite simple really,just open word.txt with ios::app (append) added to the options when opening the file.That will automaticaly put the file pointer to the end of the file and you can just write to it normally.

Another way is to open the file and use filehandle.seekp(0,ios::end); to move the pointer to the end of the file and then write the output you want to the file and it will still be at the end.

Checks the tut for the list of options,I covered them there.

Btw,then close the file when you exit or are finished with using it.There you go a new word was added to the file.

 
0
 

Interesting tutorial, however I did notice a few errors here and there.

if you output abc123 to a
text file you will see abc123 but in a binary file you may see only a bunch of black
blocks if you use notepad

I just tested this, and its not true. If you output "abc123" to a binary file, you will see "abc123" when you open that file in notepad (or any text editor). The difference between binary mode and text mode is that text mode replaces '\n' with something appropriate for the operating system you are using. Different OS's store linebreaks differently: IIRC Windows uses "\n\r" pairs, unix uses '\n' and macs use '\r'. Text mode will translate between these automatically for you, binary mode will not.

If a file handle is used
more than once without calling a close() in between them there will be errors

If you open an ofstream object with a file name, and write output to it, then call open() on the same file stream without closing it, there will be no errors. The call to open() will close the file you were writing to and just open another file. Similarly:

If you forget to close it [the fstream object]
next time you run the program it will show access errors to the C: drive (or which ever drive
you use) and you will have to restart the computer.

The destuctor of an fstream object will close any file handles for you. The last program I made left an open file handle at the end of the program. There was no problem with this, as when the destructor of the ofstream object was called, it closed the file handle automatically. Destructors are guaranteed to be called for all objects at the end of their life (for a global object at the end of the program) so there is no problem leaving file handles open. The only possible problem with this is if your program causes a seg fault or something similar, and the OS has to close it unexpectedly. In that case you have more problems than open file handles though. I do so love the fstream library. Its so hard to f*ck thinks up with it.

It was a very interesting tutorial though. You did a lot with fstream that I wouldn't have thought of.

 
0
 

True enough,about certain char being translated in the text mode.But let me remind that file<<"abc123" is a string.Try doing file<<"abc"<<123<<(char)123;

See what happens, one forgets and assumes a bit somethimes.

If you open an ofstream object with a file name, and write output to it, then call open() on the same file stream without closing it, there will be no errors. The call to open() will close the file you were writing to and just open another file. Similarly:

Try doing that with the fstream object.Also if an fstream object is declared in the global scope and it's not closed when you run the program again some access errors might be shown. A lot of my friends had this problem when they did their projects.Never declare anything global unless it's necessary.

This may not be true on diffrent compilers (or even the same one's diffrent versions).That could have happened due to a lot of reasons.But it a good practice to alway close a file handle before you exit .I just said something which might possibly happen, :).

I love the fstreams too.Thanks so much Jubulani.

 
0
 

Oops, my mistake. Calling 'open()' on an open file stream does cause errors. Sorry about that. And, true, always better safe than sorry.

:)

 
0
 

Yea, prevention is better than cure ;)

 
0
 

I've been trying to learn c++ for over 6 years and thanks to you guys and a few other web-sites cprogramming cpp-home and stuff (which I found two days ago) I finally understand...thx guys hope I can finally get my diablo 2 console based savegame editor working...the checksumming part in versions after 1.00-06 are going to kill me lol thx again

 
0
 

Sure thing.And you are most welcome ;)

 
0
 

FireNet, your like the best at this, you knew i needed it. Im working on an FStream project, this helps alot!

 
0
 

FireNet, your like the best at this, you knew i needed it. Im working on an FStream project, this helps alot!

You are most welcome ;)

I also have a new xlock (i.e my site) up.I will get the link out soon,Till then keep clicking http://xlock.ssr.be (right now it will not get you anything :( )

 
0
 

hi thanks a lot i myself i just started learning c++ on my own and believe me its not veryu easy but thanks ur tutorial i can work my way out 2 continue 2 discover the fantastic world of programming. i am one of thiose who dont wait 4 something 2 happen but 1 of those who make things happen

 
0
 

anastacia, best of luck, and if you really do what you say I can see you becoming a really good programmer.

 
0
 

hi well thanks a lot for encouraging me. as i told u i am learning c++ by myself and its really not that easy because i didnot study c or any such type of programming language. i am also learning html at the same time(very easy)
and of course i admit that 1 doesnot become a gud programmer at 1 go. i do make tons of stupid mistakes. any way looking 4ward to ur next tutorial :idea: :lol: :p :rolleyes: :o

 
0
 

You most welcome ;)

One becomes good at programming by doing and making mistakes.Failures are all stepping stones, and anyway making mistake once will help you to fix it faster if you make somthing similar again.

My next tut...... hmmmmm... I got to think about that, any suggestions ?

 
0
 

Im thinking maybe, how to do a graphical base to what you do, like how to do color box's pixels and how to change font to different styles and colors. :mrgreen: Not exactly what ima be using for a while, but i will sometime,im still learning the very basics, much like anastacia im self learning, barely touched vb, never had even seen any c code, and am doing it without any funds and using a free web tutorial worth crap, freind senoir software ebgineers and DaniWeb of course ;p , so dont feel lonly anastacia!

 
0
 

well thanks a lot for the encouragement. i really need it. well firenet maybe u can address other topics ralated to c++ or html or any other language in ur next tutorial. u can also address common troubleshooting topics generally that we may encounter while using our pc. ;) :lol: :p :surprised :o :D

You
Post:
Start New Discussion
View similar articles that have also been tagged: