13.5. Random File AccessRandom file access refers to the ability to read or modify information directly at any given position in a file. You do this by getting and setting a file position indicator, which represents the current access position in the file associated with a given stream. 13.5.1. Obtaining the Current File PositionThe following functions return the current file access position. Use one of these functions when you need to note a position in the file to return to it later.
The following example records the positions of all lines in the text file message.txt that begin with the character #: #define ARRAY_LEN 1000 long arrPos[ARRAY_LEN] = { 0L }; FILE *fp = fopen( "messages.txt", "r" ); if ( fp != NULL) { int i = 0, c1 = '\n', c2; while ( i < ARRAY_LEN && ( c2 = getc(fp) ) != EOF ) { if ( c1 == '\n' && c2 == '#' ) arrPos[i++] = ftell( fp ) - 1; c1 = c2; } /* ... */ } 13.5.2. Setting the File Access PositionThe following functions modify the file position indicator:
When working with text streamson systems that distinguish between text and binary streamsyou should always use a value obtained by calling the ftell( ) function for the offset argument, and let origin have the value SEEK_SET. The function pairs ftell( )--fseek( ) and fgetpos( )--fsetpos( ) are not mutually compatible, because the fpos_t object used by fgetpos( ) and fsetpos( ) to indicate that a file position may not have an arithmetic type. If successful, fseek( ) clears the stream's EOF flag and returns zero. A nonzero return value indicates an error. void rewind( FILE *fp ); rewind( ) sets the file position indicator to the beginning of the file and clears the stream's EOF and error flags. Except for the error flag, the call rewind(fp) is equivalent to: (void)fseek( fp, 0L, SEEK_SET ) If the file has been opened for reading and writing, you can perform either a read or a write operation after a successful call to fseek( ), fsetpos( ), or rewind( ). The following example uses an index table to store the positions of records in the file. This approach permits direct access to a record that needs to be updated. // setNewName( ): Finds a keyword in an index table // and updates the corresponding record in the file. // The file containing the records must be opened in // "update mode"; i.e., with the mode string "r+b". // Arguments: - A FILE pointer to the open data file; // - The key; // - The new name. // Return value: A pointer to the updated record, // or NULL if no such record was found. // --------------------------------------------------------------- #include <stdio.h> #include <string.h> #include "Record.h" // Defines the types Record_t, IndexEntry_t: // typedef struct { long key; char name[32]; // /* ... */ } Record_t; // typedef struct { long key, pos; } IndexEntry_t; extern IndexEntry_t indexTab[ ]; // The index table. extern int indexLen; // The number of table entries. Record_t *setNewName( FILE *fp, long key, const char *newname ) { static Record_t record; int i; for ( i = 0; i < indexLen; ++i ) { if ( key == indexTab[i].key ) break; // Found the specified key. } if ( i == indexLen ) return NULL; // No match found. // Set the file position to the record: if ( fseek( fp, indexTab[i].pos, SEEK_SET ) != 0 ) return NULL; // Positioning failed. if ( fread( &record, sizeof(Record_t), 1, fp ) != 1 ) // Read the record. return NULL; // Error on reading. if ( key != record.key ) // Test the key. return NULL; else { // Update the record: size_t size = sizeof(record.name); strncpy( record.name, newname, size-1 ); record.name[size-1] = '\0'; if ( fseek( fp, indexTab[i].pos, SEEK_SET ) != 0 ) return NULL; // Error setting file position. if ( fwrite( &record, sizeof(Record_t), 1, fp ) != 1 ) return NULL; // Error writing to file. return &record; } } The second fseek( ) call before the write operation could also be replaced with the following, moving the file pointer relative to its previous position: if ( fseek( fp, -(long)sizeof(Record_t), SEEK_CUR ) != 0 ) return NULL; // Error setting file position. |