I have a .txt file where every line is like:
id name surname 78 99 101 12 33 44
Every line I need to fill a struct of Student
. The numbers after surname
have to be stored in an array of structs Lesson
. The first number (for example 78
) is a struct field and the second number (99
) is another struct field. The pairs after surname can be a maximum of 8. I'm a bit confused since I don't know how many pairs there will be and I found this method but am not sure the way I check newline (\n) is good.
typedef struct Lesson
{
int hour;
int time;
}Lesson;
typedef struct Student
{
int id;
char name_and_surname[100];
Lesson lessons[8];
struct Student *next;
}Student;
Student s;
while (fscanf(fp, "%d %s %s", &s.id, s.name_and_surname, tmp) == 3)
{
int i = 0;
strcat(s.name_and_surname, " ");
strcat(s.name_and_surname, tmp);
while ((ch = fgetc(fp) != '\n') && fscanf(fp, "%d %d", &s.lessons[i].hour, &s.lessons[i].time) == 2)
{
i++;
}
//add s to a linked list
}
As I said in comments, if your input is organized as one record per line with variable-format lines then it is probably best to read it a whole line at a time, maybe with fgets()
, and then to parse the result into its fields. There are numerous alternatives for the parsing, among them sscanf()
, strtok()
, and strtol()
.
If you must do the job by directly scanning each field via fscanf()
then this is possible, but messy. The particular approach you present is not robust: it will not recognize newlines on lines with trailing spaces, and it will not recognize lines that are malformed by having an odd number of trailing numbers.
You could, for example, use something like this instead:
/*
* Scan one int from file 'fp' into the location pointed to by 'dest',
* skipping any leading whitespace other than newlines.
*
* Returns:
* 1 on success
* 0 if a non-numeric field is found before the next newline
* EOF if end of file or end of line is reached without encountering
* an input field, or on error
*/
int scan_one_number(FILE *fp, int *dest) {
// skip leading whitespace, but stop at a newline
while (1) {
int c = fgetc(fp);
if (c == EOF || c == '\n') {
// terminate on error, end-of-file, or end-of-line
return EOF;
} else if (!isspace(c)) {
// a non-whitespace character
// push it back onto the stream
if (ungetc(c, fp) != c) {
// error
return EOF;
}
// break out of the loop
break;
} // else it's non-newline whitespace; ignore it
}
// attempt to scan a decimal integer
return fscanf(fp, "%d", dest);
}
That would allow you to scan one number at a time with scanf
, recognizing end-of-line and malformed inputs.