Functions like <<, and fscanf, and fread, and fwrite, and fprintf, and all the other file IO functions in both C and C++, automatically move the file pointer forward as you read or write. By default, reading starts at the top of the file.
You do not have to start reading or writing at the top of a file. You can start in the middle, jump forward, and jump backward. In other words, you can control the file pointer.
When you open a file for reading or input, the value of the file pointer is automatically is set to "0", or the beginning of the input file. When you open a file for writing, the file pointer also starts at 0 and the size is set to 0. When you open a file for appending, the file pointer is set to the file size (which equals the end of the file). And, something you may not have known, you can also open a file for BOTH reading and writing.
The C function "fseek" moves the file pointer. The C++ method "seekg" move the "get" (or reading location) file pointer. Both the C and C++ versions of seek take two parameters: how far and from where. A positive value for how far moves the file pointer forward and a negative value moves backward. The "from where" can be from the beginning of the file, the current location, or the end of the file.
The predefined constants in C for "from where" are:
SEEK_SET - from beginning of file
SEEK_CUR - from current
SEEK_END - from the end of file
The predefined constants in C++ are:
ios::beg
iso::cur
iso::end
Here are two programs, one in C and one in C++, that do exactly the same thing. Both programs use the sorted records of faculty office information that you have used before. Remember that file starts with an integer count, then is followed by count number of records.
The programs fetch two records. The user enters the number of the record they want first, then indicate how many records to move forward or backwards.
If the records are numbered 1-N, then the starting location of record N is:
sizeof(int) + ( (N-1) * sizeof(record) )
Since our strange file starts with a single integer we have to add that offset.
Since we are numbering 1 to N, instead of 0 to N-1, we have to subtract 1 from
N. For example, record number 1 is really 0 bytes after the integer header.
To read record number 5, we must skip the first 4 records. The sizeof function
tells us how big our records are, hence how far to move for each record.
Note: The following code is intentionally not well commented. If you want to know how these programs work then you need to come to class.
/********************************** Program to demo using seek in C **********************************/ #include <stdio.h> #include <stdlib.h> /********************************************/ struct faculty_type { char fname[15],lname[15]; int phone, office; char dept[5]; }; /********************************************/ void main () { FILE *infile; struct faculty_type temp; int status, count, rec_num; long rec_location; infile = fopen ("faculty_sorted.dat","r"); if (infile == NULL) { fprintf(stderr, "\nError opening input\n\n"); exit (1); } fread (&count, sizeof(int), 1, infile); printf ("\ninfile contains records 1-%d\n\n", count); printf ("Which record do you want first: "); scanf ("%d", &rec_num); rec_num--; rec_location = sizeof(int) + sizeof(struct faculty_type)*rec_num; fseek (infile, rec_location, SEEK_SET); fread (&temp, sizeof(struct faculty_type), 1, infile); printf("%s %s\n", temp.fname, temp.lname); printf ("Next record (negative = back, positive = forward): "); scanf ("%d", &rec_num); rec_location = sizeof(struct faculty_type) * rec_num; status = fseek (infile, rec_location, SEEK_CUR); if (status == -1) printf ("could not move there\n"); else { fread (&temp, sizeof(struct faculty_type), 1, infile); printf("%s %s\n", temp.fname, temp.lname); } } |
/********************************************** Demo of using seek in C++ *********************************************/ #include <iostream> #include <fstream> #include <cstdlib> using namespace std; /******************************************************/ struct faculty_type { char fname[15],lname[15]; int phone, office; char dept[5]; }; /******************************************************/ int main () { ifstream infile; // the only input file long byte_location; // where to move to int count; // record count from data file int rec_number; // input : record number struct faculty_type record; infile.open ("faculty_sorted.dat", ios::in); if (!infile) { cerr << "Error opening input file"; exit (1); } infile.read(reinterpret_cast<char *> (&count), sizeof(int)); cout << "\nRead records 1-" << count << endl; cout << "Which record first: "; cin >> rec_number; rec_number--; byte_location = sizeof(int) + sizeof(struct faculty_type) * rec_number; infile.seekg (byte_location, ios::beg); infile.read(reinterpret_cast<char *> (&record), sizeof(struct faculty_type)); cout << record.fname << " " << record.lname << endl; cout << "Which record next (neg=back, pos=forward): "; cin >> rec_number; byte_location = sizeof(struct faculty_type) * rec_number; infile.seekg (byte_location, ios::cur); if (infile.fail()) cout << "could not move there\n"; else { infile.read(reinterpret_cast<char *> (&record), sizeof(struct faculty_type)); cout << record.fname << " " << record.lname << endl; } } |
Here is an example run:
> data_viewer faculty_sorted.dat | head Charles Alvis 2695 431 ACCT Keith Benson 4834 504 HELT David Bradbard 4801 430 MGMT Bob Breakfield 2685 314 BLAW Barbara Burgess 2690 427 MGMT Patrice Burleson 4835 125 MRKT Jordan Cao 2674 318 QMTH Melisa Carsten 4817 518 HRMG Clarence Coleman 2678 112 ACCT Mike Cornick 4624 410 FINC > a.out infile contains records 1-47 Which record do you want first: 1 Charles Alvis Next record (negative = back, positive = forward): 1 David Bradbard > a.out infile contains records 1-47 Which record do you want first: 5 Barbara Burgess Next record (negative = back, positive = forward): -1 Barbara Burgess > a.out infile contains records 1-47 Which record do you want first: 1 Charles Alvis Next record (negative = back, positive = forward): -2 could not move there |
In C++, to both read and write a file, declare the file variable as fstream, instead of ifstream or ofstream. Also, use "in | out" as the open mode. (The | operator ORs the bits to make the file both in and out.)
While seekg sets the location to GET input, the method seekp sets the PUT location.
The following example will change the first name of a faculty member for an indicated record number. Not seekg and read retrieve the record, then seekp and write output the record. It is not safe to try to write just part of record. If possible read and write whole records.
/********************************************** Demo of using seek to overwrite data in C++ *********************************************/ #include <iostream> #include <fstream> #include <cstdlib> #include <string.h> using namespace std; /******************************************************/ struct faculty_type { char fname[15],lname[15]; int phone, office; char dept[5]; }; /******************************************************/ int main () { fstream myfile; // the input and output file long byte_location; // where to move to int count; // record count from data file int rec_number; // input : record number struct faculty_type record; char new_name[15]; myfile.open ("faculty_sorted.dat", ios::in | ios::out); if (!myfile) { cerr << "Error opening input file"; exit (1); } /**** move fileptr to top and read record count *****/ myfile.seekg (0, ios::beg); myfile.read(reinterpret_cast<char *> (&count), sizeof(int)); cout << "\nRead records 1-" << count << endl; /***** get user input *****/ cout << "Which record to change: "; cin >> rec_number; cout << "Enter new First Name: "; cin >> new_name; /***** Retrieve existing record *****/ byte_location = sizeof(int) + (sizeof(struct faculty_type) * (rec_number - 1)); myfile.seekg (byte_location, ios::beg); myfile.read(reinterpret_cast<char *> (&record), sizeof(struct faculty_type)); /***** Reset that record *****/ strcpy (record.fname, new_name); myfile.seekp (byte_location, ios::beg); myfile.write(reinterpret_cast<char *> (&record), sizeof(struct faculty_type)); } |
And here is the output from one run, changing Professor Alvis' name from Charles to Chuck.
> a.out Read records 1-47 Which record to change: 1 Enter new First Name: Chuck > data_viewer faculty_sorted.dat | head Chuck Alvis 2695 431 ACCT Keith Benson 4834 504 HELT David Bradbard 4801 430 MGMT Bob Breakfield 2685 314 BLAW Barbara Burgess 2690 427 MGMT Patrice Burleson 4835 125 MRKT Jordan Cao 2674 318 QMTH Melisa Carsten 4817 518 HRMG Clarence Coleman 2678 112 ACCT Mike Cornick 4624 410 FINC |