If you are going to attempt to read the varying data with fscanf, then the format string will be the key to your success (or failure). If each of the rows you need to collect have an identical format, then you can attempt to read the data with fscanf (while you gain flexibility by decoupling your read from your parse & validation by using fgets and sscanf). However, as long as you have a way to keep the lines you need, and skip those that you don't, then fscanf is available as a tool.
In this case with your example data LYF_HKN has done a good job in his answer accounting for all the information in each of the lines you need. I would just add a small variant and use something like:
char *fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
The rest is simply looping over the lines in your input file and indexing the students you add to your struct. You will need to capture the return of fscanf and compare it with your needed match count of 10 to validate that each of your values received input. You also need to skip the header line and any other lines not holding student data. You can accomplish this by reading so long as fscanf does not encounter EOF setting an error condition on the stream. You then simply check the match count (the return) against your anticipated 10 to indicate whether it was a valid line of student data or not.
Putting this together, you could do something like the following:
#include <stdio.h>
/* constants for use in your code */
enum { YN = 2, DOB = 11, SS = 12, NM = 32 };
typedef struct {
char last[NM], first[NM], ss[SS], dob[DOB], yn[YN];
double qavg, lavg, ex1, ex2, fex;
} stnt;
int main (int argc, char **argv) {
stnt s[8] = {{ .first = "" }};
int cnv = 0, n = 0;
char *fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
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;
}
/* read each line, if the match-count is 10 increment index */
while ((cnv=fscanf (fp, fmt, s[n].last, s[n].first, s[n].ss, s[n].dob, s[n].yn,
&s[n].qavg, &s[n].lavg, &s[n].ex1, &s[n].ex2, &s[n].fex)) != EOF)
if (cnv == 10) n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < n; i++) /* output the student information */
printf ("\n student : %s %s\n ss number : %s\n D.O.B. : %s\n"
" yes/no : %s\n quiz avg : %.2lf\n lab avg : %.2lf\n"
" exam 1 : %.2lf\n exam 2 : %.2lf\n final : %.2lf\n",
s[i].first, s[i].last, s[i].ss, s[i].dob, s[i].yn, s[i].qavg,
s[i].lavg, s[i].ex1, s[i].ex2, s[i].fex);
return 0;
}
Using fgets and sscanf
Given the discussion and the comments regarding the use of fgets and sscanf, it is worth dropping a short example making use of that approach, which I prefer, in addition to the fscanf example. The code is roughly the same except for the addition of a buffer buf to hold the line of user input and the control of the loop based on the return of fgets. Other than that, the replacement of fscanf with sscanf is nothing more than substituting the buffer in place of the file stream in the sscanf call. Both fscanf and sscanf return the match count being the number of successful conversions that took place based on the conversion specifiers present in the format string.
#include <stdio.h>
/* constants for use in your code */
enum { YN = 2, DOB = 11, SS = 12, NM = 32, MAXC = 256 };
typedef struct {
char last[NM], first[NM], ss[SS], dob[DOB], yn[YN];
double qavg, lavg, ex1, ex2, fex;
} stnt;
int main (int argc, char **argv) {
stnt s[8] = {{ .first = "" }};
int n = 0;
char buf[MAXC] = "",
*fmt = " %*[^\"]\"%[^,], %[^\"]\" %s %s %[YN] %lf %lf %lf %lf %lf";
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;
}
/* read each line, if the match-count is 10 increment index */
while (fgets (buf, MAXC, fp))
if (sscanf (buf, fmt, s[n].last, s[n].first, s[n].ss, s[n].dob, s[n].yn,
&s[n].qavg, &s[n].lavg, &s[n].ex1, &s[n].ex2, &s[n].fex) == 10)
n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < n; i++) /* output the student information */
printf ("\n student : %s %s\n ss number : %s\n D.O.B. : %s\n"
" yes/no : %s\n quiz avg : %.2lf\n lab avg : %.2lf\n"
" exam 1 : %.2lf\n exam 2 : %.2lf\n final : %.2lf\n",
s[i].first, s[i].last, s[i].ss, s[i].dob, s[i].yn, s[i].qavg,
s[i].lavg, s[i].ex1, s[i].ex2, s[i].fex);
return 0;
}
Example Use/Output
Using your data (with either), you would end up with output similar to the following:
$ ./bin/rdstudents <dat/lab4text.txt
student : Christopher Jones
ss number : 162-74-2381
D.O.B. : 9/12/1995
yes/no : Y
quiz avg : 51.67
lab avg : 72.50
exam 1 : 77.00
exam 2 : 68.50
final : 61.00
student : Sarah Lee Abrahamson
ss number : 127-49-0853
D.O.B. : 11/5/1993
yes/no : N
quiz avg : 87.10
lab avg : 79.33
exam 1 : 64.25
exam 2 : 84.00
final : 72.50
student : Adreana Parker-Jones
ss number : 230-38-1234
D.O.B. : 3/1/1996
yes/no : Y
quiz avg : 75.23
lab avg : 81.04
exam 1 : 78.50
exam 2 : 80.00
final : 85.25
student : Joshua Ellis
ss number : 186-27-1372
D.O.B. : 7/31/1988
yes/no : Y
quiz avg : 85.23
lab avg : 94.90
exam 1 : 85.00
exam 2 : 92.00
final : 94.25
"%c"format (with or without any modifier) will not skip leading white-space. Try putting a space in the format string before the"%*c"you use to skip the pipe characters. Also check whatscanfreturns.scanfor can you use a normal line-oriented input function likefgetsorgetlineand then pass the line tosscanf(or simply walk a pair of pointers through the buffer).scan()and OP is having troubles. To get past "have no idea on how to fix it.", test the return value of eachscanf()against expectations.if (scanf("%*d-%*d-%d", &SSN[i]) == 1) Good() else Bad();