Yes, such abstraction is usually best. However, in C, you must build your own.
I've taken some liberties here.
1. First, I changed your direction type to an enumeration.
2. Second, I added a whole lot of error checking. (I am assuming this is not a school project. Please don't make me wrong.)
Some notes:
1. Don't name files other than C source code "anything.c". I renamed "maps.c" to "maps.txt".
2. Don't use C++ comments in a C program.
3. Don't forget to close files when you are done with them.
4. This code aborts on error, but does not inform you that an error actually occurred. Hence, if the nth record contains an error, you will only get n-1 records out of the file, and main() won't know any different. There are a number of ways to settle this error... the easiest being to check to see if any record has a dest1 or dest2 >= the number of records read. You'll have to do that.
5. Care is taken not to hardcode the length of any string in the readRooms() function. (Note the use of the sizeof operator.) Keep in mind this only works for char strings. (Unicode strings would need a bit more help.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef enum {
d_none, d_north, d_south, d_east, d_west, d_error = -1
} direction_t;
const char *directions[] = {
"none", "north", "south", "east", "west"
};
typedef struct {
int room_num;
char room_name[ 80 ];
char desc[ 1000 ];
int num_links;
direction_t direction1;
int dest1;
direction_t direction2;
int dest2;
} roomStruct;
direction_t str_to_direction( const char *d );
int readRooms( const char *filename, roomStruct rooms[], int max_rooms );
/* -----------------------------------------------------------------
main
----------------------------------------------------------------- */
int main () {
int num_rooms;
roomStruct rooms[ 100 ];
num_rooms = readRooms( "maps.txt", rooms, 100 );
printf( "number of rooms = %d", num_rooms );
return 0;
}
/* -----------------------------------------------------------------
fgetline
--------------------------------------------------------------------
Get an entire line from file, filling 'max' characters with
the result and tossing any extra, stripping the newline,
guaranteeing the string is nul-terminated,
and returning success or failure.
*/
int fgetline( FILE *f, char *s, int max ) {
char c;
int i;
/* Get the line from file */
if (fgets( s, max, f ) == NULL) return 0;
/* Make sure the entire line was read */
i = strlen( s ) -1;
if (s[ i ] != '\n') {
while ((c = fgetc( f )) != '\n')
if (c == EOF) break;
}
/* Strip the trailing newline */
else s[ i ] = '\0';
return 1;
}
/* -----------------------------------------------------------------
mstricmp
--------------------------------------------------------------------
The POSIX stricmp function is deprecated and not always
available anyway... so we roll our own here.
*/
int mstricmp( const char *s, const char *t ) {
int i;
char *ss, *tt;
for (
ss = (char *)s, tt = (char *)t;
*ss || *tt;
ss++, tt++
) {
i = tolower( *ss ) - tolower( *tt );
if (i != 0) return i;
}
return 0;
}
/* -----------------------------------------------------------------
str_to_direction
--------------------------------------------------------------------
Convert a character string into a direction_t.
Takes any string that is <= a direction name without
case-sensitivity.
Hence, "n" == "north", as do "no", "NOR", etc.
*/
direction_t str_to_direction( const char *d ) {
direction_t i;
for (i = d_none; i <= d_west; i++)
if (mstricmp( d, directions[ i ] ) <= 0) return i;
return d_error;
}
/* -----------------------------------------------------------------
readRooms
--------------------------------------------------------------------
Read records from file. Returns the number of complete records read.
*/
int readRooms( const char *filename, roomStruct rooms[], int max_rooms ) {
char s[ 20 ];
char *p;
FILE *f;
int i = 0;
if ((f = fopen( filename, "r" )) == NULL) return 0;
do {
/* get the room number */
if (!fgetline( f, s, 20 )) break;
rooms[ i ].room_num = strtol( s, &p, 10 );
if (*p != '\0') break;
/* get the room name */
if (!fgetline( f, rooms[ i ].room_name, sizeof( rooms[ i ].room_name ) )) break;
/* get the room description */
if (!fgetline( f, rooms[ i ].desc, sizeof( rooms[ i ].desc ) )) break;
/* get the number of links */
if (!fgetline( f, s, 20 )) break;
rooms[ i ].num_links = strtol( s, &p, 10 );
if (*p != '\0') break;
/* get direction 1 */
if (!fgetline( f, s, 20 )) break;
if ((rooms[ i ].direction1 = str_to_direction( s )) == d_error) break;
/* get destination 1 */
if (!fgetline( f, s, 20 )) break;
rooms[ i ].dest1 = strtol( s, &p, 10 );
if (*p != '\0') break;
/* get direction 2 */
if (!fgetline( f, s, 20 )) break;
if ((rooms[ i ].direction2 = str_to_direction( s )) == d_error) break;
/* get destination 2 */
if (!fgetline( f, s, 20 )) break;
rooms[ i ].dest2 = strtol( s, &p, 10 );
if (*p != '\0') break;
if (++i >= max_rooms) break;
} while (!feof( f ));
fclose( f );
return i;
}
The purpose for posting all this wasn't to make your life thought-free. It was to give you a template upon which to work your program further. It provides examples of:
1. Good I/O.
2. Use of enums and conversion. (enum to string: directions[ d_north ] )
3. Use of a loop.
4. Care when handling maxima (i.e. you can't read more records than you have room for --currently at 100).
5. capacity for good error checking.
Well, that's enough. Enjoy.