860 likes | 1.06k Views
File Handling in C++. Why do we need Files?. All the programs we have looked at so far use input only from the keyboard, and output only to the screen Data that is held in variables is temporary and is lost when the program execution is finished
E N D
Why do we need Files? • All the programs we have looked at so far use input only from the keyboard, and output only to the screen • Data that is held in variables is temporary and is lost when the program execution is finished • In order to store data permanently, we need to store it in files on the hard disk • The easiest way to think about a file is as a linear sequence of characters ‘W’ ‘h’ ‘y’ ‘ ’ ‘d’ ‘o’ …. CPS235: FilesAndStreams
The Data Hierarchy CPS235: FilesAndStreams
C++ Streams • Stream • A transfer of information in the form of a sequence of bytes • I/O Operations • Input stream: A stream that flows from an input device to the main memory ( from: keyboard, disk drive, scanner or network connection) • Output stream: A stream that flows from the main memory to an output device ( to: screen, printer, disk drive or network connection) CPS235: FilesAndStreams
Program Output Stream (ostream) Input Stream (istream) I/O Devices CPS235: FilesAndStreams
Program Output Stream (ofstream) Input Stream (ifstream) File on Disk CPS235: FilesAndStreams
Headers required for stream processing • <iostream.h> (cout, cin, cerr, clog) • <fstream.h> • class ifstream - input from file • class ofstream – output to file • class fstream - either input or output from/to file CPS235: FilesAndStreams
Creating Streams • Before we can use an input or output stream in a program, we must "create" it //create an input stream ifstream in_stream; //create an output stream ofstream out_stream; CPS235: FilesAndStreams
Connecting Streams to Files • Having created a stream, we can connect it to a file using the member function"open(...)” • in_stream.open(“file.txt"); • out_stream.open(“file.txt"); • it also deletes the previous contents of the file CPS235: FilesAndStreams
Creating and connecting streams to files in one statement ifstream in_stream(“file.txt”); ofstream out_stream(“file.txt”); CPS235: FilesAndStreams
Disconnecting Streams from Files • To disconnect a stream from a file, we can use the member function “close(...)” • in_stream.close(); • out_stream.close(); • Adds an "end-of-file" marker at the end of the file, so if no data has been output to “file.txt” since "out_stream" was connected to it, we have this situation CPS235: FilesAndStreams
Checking for Failure with File Commands void main() { ifstream in_stream; in_stream.open("Lecture.txt"); if (in_stream.fail()) // or if(!in_stream) { cout << "Sorry, the file couldn't be opened!\n"; exit(1); } //.... } CPS235: FilesAndStreams
Character Input Input using get() • we can extract or read single characters from the file using the member function "get(...)“ • in_stream.get(ch); has two effects: • the variable "ch" is assigned the value "'4'", and • the ifstream "in_stream" is re- positioned so as to be ready to input the next character in the file. • Diagramatically, the new situation is: CPS235: FilesAndStreams
Character Output Output using put() • We can write single characters to a file opened via an ofstream using the member function "put(...)“ • out_stream.put('4'); changes the state to: CPS235: FilesAndStreams
Checking for the end of an input file • The eof() function returns true if the end-of-file is reached • This function can be used in a loop to determine how many times the get() function should be called to read the complete input file CPS235: FilesAndStreams
Example • Write code to copy the contents (character by character) of a file “old.txt” simultaneously to the screen and to another file “new.txt” CPS235: FilesAndStreams
#include <iostream> #include <fstream> #include <conio> #include <stdlib> void main() { char character; ifstream in_stream; //create input stream ofstream out_stream; //create output stream in_stream.open("old.txt"); //connect to file if(!in_stream) //if file opening fails { cerr<<"file could not be opened"; exit(1); } out_stream.open("new.txt"); //connect to file in_stream.get(character); //read 1 char from file while (!in_stream.eof()) //loop till eof { cout << character; //display to screen out_stream.put(character); //write to file in_stream.get(character); //read next char } out_stream.close(); //disconnect stream in_stream.close(); //disconnect stream } CPS235: FilesAndStreams
Input and output using >> and << void main() { char character; int number = 51; int count = 0; ofstream out_stream; ifstream in_stream1; ifstream in_stream2; /* Create the file */ out_stream.open("Integers.txt"); for (count = 1 ; count <= 5 ; count++) out_stream << number++ << ' '; out_stream.close(); CPS235: FilesAndStreams
Input and output using >> and << /* Count the integers in the file */ in_stream1.open("Integers.txt"); count = 0; in_stream1 >> number; while (!in_stream1.eof()) { count++; in_stream1 >> number; } in_stream1.close(); cout << "There are " << count << " integers in the file,\n"; CPS235: FilesAndStreams
Input and output using >> and << /* Count the non-blank characters */ in_stream2.open("Integers.txt"); count = 0; in_stream2 >> character; while (!in_stream2.eof()) { count++; in_stream2 >> character; } in_stream2.close(); cout << "represented using " << count << " characters.\n"; } CPS235: FilesAndStreams
Input and output using >> and << Another Example void main() { ofstream outClientFile( "clients.dat", ios::out ); if ( !outClientFile ) { cerr << "File could not be opened”; exit(1); } cout << "Enter the account, name, and balance.\n” << "Enter end-of-file to end input.\n? "; //contd on next slide… CPS235: FilesAndStreams
Input and output using >> and << //…contd from previous slide int account; char name[ 30 ]; float balance; while ( cin >> account >> name >> balance ) { outClientFile << account << ' ' << name << ' ' << balance << '\n'; cout << "? "; } getch(); } CPS235: FilesAndStreams
File open modes • ios:: app - (append) write all output to the end of file • ios:: ate - data can be written anywhere in the file • ios:: binary - read/write data in binary format • ios:: in - (input) open a file for input • ios::out - (output) open a file for output • ios:: trunc -(truncate) discard the files’ contents if it exists • ios::nocreate - if the file does NOT exist, the open operation fails • ios::noreplace - if the file exists, the open operation fails CPS235: FilesAndStreams
File open modes (defaults) • class default mode parameter • ofstream ios::out • ifstream ios::in CPS235: FilesAndStreams
fstream class • An object of fstream opens a file for reading and writing simultaneously, so we can write something like fstream outClientFile; int account; char name[20]; float balance; outClientFile.open( "clients.dat",ios::out | ios::in ); //specifying multiple modes outClientFile>>account>>name>>balance; cout<<account<<" "<<name<<" "<<balance; outClientFile<<“writing something new!"; CPS235: FilesAndStreams
Reading and printing a sequential file #include <iostream.h> #include <fstream.h> #include <iomanip.h> #include <stdlib.h> void outputLine( int, const char *, double ); int main() { // ifstream constructor opens the file ifstream inClientFile (“Z:\\MyFolder\\clients.dat", ios::in ); if ( !inClientFile ) { cerr << "File could not be opened\n"; exit( 1 ); } CPS235: FilesAndStreams
int account; char name[ 30 ]; double balance; cout << setiosflags( ios::left ) << setw(10) << "Account“ << setw( 13 ) << "Name" << "Balance\n"; while ( inClientFile >> account >> name >> balance ) outputLine( account, name, balance ); return 0; // ifstream destructor closes the file } void outputLine( int acct, const char *name, double bal ) { cout << setiosflags( ios::left )<< setw(10) << acct << setw( 13 ) << name << setw( 7 ) << setprecision( 2 ) << resetiosflags( ios::left )<< setiosflags( ios::fixed | ios::showpoint )<< bal << '\n‘;} CPS235: FilesAndStreams
Exercise • Design and implement a program that computes the average of a sequence of numbers stored in a file CPS235: FilesAndStreams
Algorithm 0. Prompt for input file name 1. Read name of input file from cin into inFileName 2. Open connection named fin to file named in inFileName 3. Initialize sum, count to zero. 4. Read a value from fin into number 5. Loop: b. If no values left, terminate repetition. c. Add number to sum. d. Increment count. End loop. 6. Close fin. 7. If count > 0: display sum / count. Else display error message End if CPS235: FilesAndStreams
#include <iostream> // cin, cout, ... #include <fstream> // ifstream, ofstream, ... #include <string> int main() { cout << “\nTo average the numbers in an input file,enter the name of the file: “; string inFileName; cin >> inFileName; ifstream fin(inFileName.data()); // open the //connection if(!fin) { cerr<<“File could not be opened”; exit(1); } double number, sum = 0.0; // variables for int count = 0; // computing //average CPS235: FilesAndStreams
fin>>number; //read number while(!fin.eof()) { sum += number; // add it to sum count++; fin >> number; // read next number } fin.close(); // close fstream if (count > 0) cout << “\nThe average of the values in “ << inFileName << “ is “ << sum/count << endl; else cout << “\n*** No values found in file “ << inFileName << endl; } CPS235: FilesAndStreams
Repositioning the file-position pointer • <istream> and <ostream> classes provide member functions for repositioning the file pointer (the byte number of the next byte in the file to be read or to be written.) • These member functions are: • seekg(seek get) for istream class • seekp (seek put) for ostream class CPS235: FilesAndStreams
Examples of moving a file pointer • fileObject.seekg( 0 ); • reposition the file-position pointer to the beginning of the file (location 0) attached to fileObject • fileObject.seekg(n) • position to the nth byte of fileObject (assumes ios::beg) • fileObject.seekg( n,ios::curr ); • position n bytes forward in fileObject • fileObject.seekg( n, ios::end ); • position n bytes back from end of fileObject • fileObject.seekg( 0, ios::end ); • position at end of fileObject The same operations can be performed using ostream member function seekp CPS235: FilesAndStreams
tellg()and tellp() • Member functions tellg() and tellp() are provided to return the current locations of the get and put pointers, respectively • long location = fileObject.tellg(); CPS235: FilesAndStreams
Problems with Sequential Files • Data that is formatted and written to a sequential file cannot be modified easily without the risk of destroying other data in the file • If we want to modify a record of data, the new data may be longer than the old one and it could overwrite parts of the record following it • Sequential files are inappropriate for so-called “instant access” applications in which a particular record of information must be located immediately. • These applications include banking systems, point-of-sale systems, airline reservation systems, (or any data-base system) CPS235: FilesAndStreams
Random Access Files • Instant access is possible with random access files • Individual records of a random access file can be accessed directly (and quickly) without searching many other records CPS235: FilesAndStreams
Binary I/O • We have seen examples of formatted I/O where each number is stored as a sequence of characters but it is not always efficient • It is more efficient to use binary I/O in which numbers are stored in the form of bytes • So, in binary I/O, an int is always stored in 4 bytes whereas its text version might be 12345 which requires 5 bytes • For binary I/O • the file should be opened in the binary mode (ios::binary) • member functions read() of the ostream class and write() of the istream class will be used for reading/writing data from/to disk CPS235: FilesAndStreams
Writing Bytes with ostream Member Function write() • To write an int variable num to the file, use: outFile.write( reinterpret_cast< const char * >(&num ), sizeof( num) ); OR • outFile.write((char *)&num , sizeof( num) ); • This writes the binary version of the number’s 4 bytes • Function write treats its first argument as a group of bytes by viewing the object in memory as a const char*, which is a pointer to a byte (remember that a char is one byte) • Starting from that location, function write outputs the number of bytes specified by its second argument, an integer of type size_t CPS235: FilesAndStreams
reinterpret_cast • Unfortunately, most pointers that we pass to function write as the first argument are not of type const char * • To output objects of other types, we must convert the pointers to those objects to type const char * • C++ provides the reinterpret_cast operator for cases like this in which a pointer of one type must be cast to an unrelated pointer type • You can also use this cast operator to convert between pointer and integer types, and vice versa CPS235: FilesAndStreams
reinterpret_cast • A reinterpret_cast is performed at compile time and does not change the value of the object to which its operand points • Instead, it requests that the compiler reinterpret the operand as the target type (specified in the angle brackets following the keyword reinterpret_cast). • Here we are using a reinterpret case to convert an int* (the type of the expression &num) to a const char* • The same conversion would have to be done in case of the read() function of the istream class CPS235: FilesAndStreams
Example of a Program that Creates a Random Access File //file: clientData.h struct clientData { int accountNumber; char lastName[ 15 ]; char firstName[ 10 ]; float balance; }; CPS235: FilesAndStreams
// Creating a random access file #include <iostream.h> #include <fstream.h> #include <stdlib.h> #include “clientData.h" void main() { ofstream outCredit ("credit.dat” , ios::binary); if ( !outCredit ) { cerr << "File could not be opened." exit( 1 ); } clientData blankClient = { 0, "", "", 0.0 };for ( int i = 0; i < 100; i++ )outCredit.write((const char*) &blankClient, sizeof( clientData ) );} CPS235: FilesAndStreams
Writing data randomly to a random file #include <iostream.h> #include <fstream.h> #include <stdlib.h> #include "clientData.h" void main() { ofstream outCredit( "credit.dat",ios::ate|ios::binary ); if ( !outCredit ) { cerr << "File could not be opened." << endl; exit( 1 ); } CPS235: FilesAndStreams
cout << "Enter account number”(1 to 100, 0 to end input)\n? "; clientData client; cin >> client.accountNumber; while ( client.accountNumber > 0 && client.accountNumber <= 100 ) {cout << "Enter lastname, firstname, balance\n? ";cin >> client.lastName >> client.firstName >> client.balance;outCredit.seekp( ( client.accountNumber - 1 ) *sizeof( clientData ) );outCredit.write((const char*) &client , sizeof( clientData ) );cout << "Enter account number\n? ";cin >> client.accountNumber; } CPS235: FilesAndStreams
Reading data from a random file sequentially #include <iostream.h> #include <iomanip.h> #include <fstream.h> #include <stdlib.h> #include “clientData.h" void outputLine( ostream&, const clientData & ); void main() { ifstream inCredit( "credit.dat", ios::in ); if ( !inCredit ) { cerr << "File could not be opened." << endl; exit( 1 ); } CPS235: FilesAndStreams
cout << setiosflags( ios::left ) << setw( 10 ) << "Account” << setw( 16 ) << "Last Name" << setw( 11 )<< "First Name" << resetiosflags( ios::left ) << setw( 10 ) << "Balance" << endl; clientData client; inCredit.read( (char*) &client, sizeof( clientData ) ); while ( inCredit && !inCredit.eof() ) { if ( client.accountNumber != 0 ) outputLine( cout, client ); inCredit.read( (char*) &client, sizeof( clientData ) ); } } CPS235: FilesAndStreams
void outputLine( ostream &output, const clientData &c ){ output << setiosflags( ios::left ) << setw( 10 ) << c.accountNumber << setw( 16 ) << c.lastName << setw( 11 ) << c.firstName << setw( 10 ) << setprecision( 2 ) << resetiosflags( ios::left )<< setiosflags( ios::fixed | ios::showpoint ) << c.balance << '\n';} CPS235: FilesAndStreams
Updating a record using random access void main() { fstream File ("credit.dat", ios::binary|ios::out|ios::in); int accNumber; cout<<"Enter the number of the account you wish to modify"; cin>>accNumber; // move file-position pointer to correct record in file inFile.seekg( ( accNumber - 1 ) * sizeof( ClientData ) ); // read the required record from file ClientData client; inFile.read( (char*)&client, sizeof( ClientData ) ); CPS235: FilesAndStreams
// update record if ( client.accountNumber != 0 ) { outputLine( cout, client ); // display the record // request user to specify transaction cout << "\nEnter charge(+)or payment(-):"; double transaction; // charge or payment cin >> transaction; client.balance += transaction; outputLine( cout, client ); // display the modified record // move file-position pointer to correct record in file File.seekp( ( accNumber - 1 ) * sizeof( ClientData ) ); // write updated record over old record in file File.write( (char*) &client, sizeof( ClientData ) ); }// end if CPS235: FilesAndStreams