Reputation: 31
I'm having problems figuring out how to read a name from a file that has numbers using a loop. For example
Kobe Bryant 81 62 60
I know how to individually scan each string and number into a variable but I'm stumped on how to do it more simple.
while ((chh = fgetc(input)) != EOF)
{
printf("%c", chh);
}
This reads each line till EOF but i'm not sure how to make it stop at kobe bryant. One way I could approach this is to make a c string in a loop, make it stop some how and set it equal to a variable.
side note: What if there were 2 other player names under kobe Bryant with numbers, how would I approach that?
Thanks!
Upvotes: 0
Views: 102
Reputation: 84642
Before you worry about reading multiple lines containing names, work on reading one properly first. When using character-oriented input, you have two basic approaches for filling your array. (1) use an array index and as each valid character is read, add to your array at the current index; or (2) use a pointer to your array and increment the pointer as each valid character is added to the array.
You have a minimum two conditions to validate when reading/storing each character, (1) that you are not attempting to write beyond the end of your array (allowing space for the nul-terminating character, and (2) that you terminate your read on EOF
.
The rest is just cold logic of (1) when to stop adding characters to your array and (2) where the nul-terminating character should be placed. With any learning exercise like this, the easiest way to do it is to take out a pencil and piece of paper, write an example line down and then step through each character, coding your conditions as you go. I'm not kidding, write out something like the following on paper and work through the logic:
|K|o|b|e| |B|r|y|a|n|t| |8|1| |6|2| |6|0|\n|
As you read each character, determine what the logic needs to be to determine whether to store the character, and whether to keep reading, or whether to read/discard all remaining characters in the line (beginning when you encounter your first invalid character). You must also determine how you will handle the '\n'
when reading multiple lines.
An example using array indexes would be:
enum { MAXL = 64 }; /* constant for max name length */
...
char name[MAXL] = ""; /* char array to hold name */
int c = 0, idx = 0; /* var for char & array index */
while (idx + 2 < MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
name[idx++] = c; /* write char to name, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (idx) { /* if name contains chars - check last char */
if (name[idx-1] == '.' || name[idx-1] == ' ')
name[--idx] = 0; /* if not letter, overwrite prior */
else
name[idx] = 0; /* nul-terminate at current idx */
printf (" name: '%s'\n", name);
}
The same body of logic using a pointer and end-pointer would be:
char name[MAXL] = ""; /* char array to hold name */
char *p, *ep; /* current pointer & end pointer */
int c = 0; /* var for current character */
p = ep = name; /* initialize both to name */
while (p + 2 < name + MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
ep = p; /* set end ptr to current positon */
*p++ = c; /* write char to current, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (p > name) { /* if name contains chars */
if (*ep == '.' || *ep == ' ')
*ep = 0; /* nul-terminate prior */
else
*p = 0; /* nul-terminate current */
printf (" name: %s\n", name);
}
To read multiple names, you simply need to wrap the code for reading each line, in a continual loop and check whether c == EOF
at the end. e.g.
for (;;) { /* loop continually over each line in file */
...
if (c == EOF) /* if EOF, break */
break;
}
A short example putting all the pieces together (using pointers instead of indexes) could be something like:
#include <stdio.h>
enum { MAXL = 64 }; /* constant for max name length */
int main (int argc, char **argv) {
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
for (;;) { /* loop continually over each line in file */
char name[MAXL] = ""; /* char array to hold name */
char *p, *ep; /* current pointer & end pointer */
int c = 0; /* var for current character */
p = ep = name; /* initialize both to name */
while (p + 2 < name + MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
ep = p; /* set end ptr to current positon */
*p++ = c; /* write char to current, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (p > name) { /* if name contains chars */
if (*ep == '.' || *ep == ' ')
*ep = 0; /* nul-terminate prior */
else
*p = 0; /* nul-terminate current */
printf (" name: %s\n", name);
}
if (c == EOF) /* if EOF, break */
break;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Example Input
$ cat dat/namesnums.txt
Kobe Bryant 81 62 60
John Doe 21 34
Alfred C. Jones 882
Example Use/Output
$ ./bin/fgetcname <dat/namesnums.txt
name: Kobe Bryant
name: John Doe
name: Alfred C. Jones
(note: the code also accounts for lines with no numbers following the name)
Look over the examples. There truly is no shortcut. When using character oriented input, you must account for each anticipated character in line. (to make your code even more robust, you should account for each unanticipated character as well, but that is left for another day). Let me know if you have questions.
Upvotes: 1