Another standard way to approach the problem is simply to use a pointer set to the end of the line, and then backup -- trimming whitespace (and nul-terminating) until a letter or digit is found -- then just backing up until you find then next whitespace. Since your pointer is now pointing to the last whitepace before weight
, just save a pointer to current + 1
which will point to the beginning of weight
(repeat again for height
)
You have now saved a pointer to height
and your pointer now points to the last whitespace before height
. Just keep backing up over whitespace (nul-terminating as you go) until your either reach the next letter or digit (which will be the end of name) or you hit the beginning of the string (something went wrong and you didn't find `name, height, weight).
Since you have been nul-terminating up to that point, you know the buffer you read now contains only the name
, so you can simply print your output and go read the next line. There is absolutely nothing you cannot parse simply by walking a pointer (or pair of pointers) down a string (or up a string in this case)
Putting it altogether, you could do something similar to the following:
#include <stdio.h>
#include <ctype.h>
enum { MAXHW = 2, MAXC = 512 }; /* if you need constants, define them */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
char *p = buf, /* pointer to buf */
*hw[MAXHW] = {NULL}; /* array of MAXHW pointers */
int ndx = 0; /* array index */
while (*p) p++; /* find end of buf */
p--; /* backup to last char */
while (p > buf) { /* while pointer within buf */
/* loop checking if char is a trailing space, tab, newline, ... */
while (p > buf && isspace (*p))
*p-- = 0; /* overwrite with nul-terminating char */
/* loop checking each char is a letter or digit */
while (p > buf && isalnum (*p))
p--; /* just backup to previous char */
hw[ndx++] = p + 1; /* space before word found, save ptr to word */
if (p != buf) /* if not at beginning */
*p = 0; /* nul-terminate */
else
break; /* at beginning - bail */
if (ndx == MAXHW) /* if both h & w filled */
break; /* bail */
p--; /* backup to previous and keep going */
}
if (p > buf && ndx == MAXHW) { /* not at beginning & H & W found */
p--; /* backup to previous */
/* trim trailing whitespace to end of name */
while (p > buf && isspace (*p))
*p-- = 0;
}
/* output results */
printf ("name: %s, height: %s, weight: %s\n", buf, hw[1], hw[0]);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Example Use/Output
$ ./bin/readhw <dat/hw.dat
name: Yuri, height: 164, weight: 80
name: Lai San Young, height: 155, weight: 60
name: John Wayne, height: 180, weight: 93
There are many, many different ways you can do this. You could have not used ctype.h
and checked, e.g. if (p > buf && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
using the explicit checks for whitespace in lieu of isspace()
, etc.. You could have use strpbrk
to scan forward in the string for your first digit and worked both forwards and backwards from there. No matter how you do it, the key is to just write your string out on a piece of paper and use a pencil to move your pointer position forward and back while you figure out the testing and indexing you need.
Look things over an let me know if you have further questions.