在检查新行时使用 fscanf 填充文件中的结构数组(反馈)
Filling an array of struct from a file with fscanf while checking new line (feedback)
我有一个 .txt 文件,其中每一行都是这样的:
id name surname 78 99 101 12 33 44
每一行我都需要填充一个Student
的结构。 surname
之后的数字必须存储在结构数组 Lesson
中。第一个数字(例如 78
)是一个结构字段,第二个数字(99
)是另一个结构字段。姓氏后面的对最多可以有 8 个。我有点困惑,因为我不知道会有多少对,我找到了这个方法,但不确定我检查换行符 (\n) 的方式是否正确。
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
}
我觉得只要你知道Lesson的输入必须是成对的,不会是奇数就可以了。
在这种情况下,您可以用 0 或 null 甚至 -1 左右填充数组 lessons 的末尾。你这样做是因为你可能想在之后打印它。
类似的东西:
for (j = 0; j < i; j++)
{
s.lessons[j].hour = 0;
s.lessons[j].time = 0;
}
正如我在评论中所说,如果您的输入被组织为每行一条记录且格式可变的行,那么最好一次读取整行,也许使用 fgets()
,并且然后将结果解析到它的字段中。有许多可供选择的解析方法,其中包括 sscanf()
、strtok()
和 strtol()
.
如果您必须通过fscanf()
直接扫描每个字段来完成这项工作,那么这是可能的,但很麻烦。您提出的特定方法并不可靠:它不会识别带有尾随空格的行上的换行符,也不会识别因尾随数字为奇数而格式错误的行。
例如,您可以改用这样的东西:
/*
* 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);
}
这将允许您使用 scanf
一次扫描一个数字,识别行尾和格式错误的输入。
我有一个 .txt 文件,其中每一行都是这样的:
id name surname 78 99 101 12 33 44
每一行我都需要填充一个Student
的结构。 surname
之后的数字必须存储在结构数组 Lesson
中。第一个数字(例如 78
)是一个结构字段,第二个数字(99
)是另一个结构字段。姓氏后面的对最多可以有 8 个。我有点困惑,因为我不知道会有多少对,我找到了这个方法,但不确定我检查换行符 (\n) 的方式是否正确。
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
}
我觉得只要你知道Lesson的输入必须是成对的,不会是奇数就可以了。 在这种情况下,您可以用 0 或 null 甚至 -1 左右填充数组 lessons 的末尾。你这样做是因为你可能想在之后打印它。 类似的东西:
for (j = 0; j < i; j++)
{
s.lessons[j].hour = 0;
s.lessons[j].time = 0;
}
正如我在评论中所说,如果您的输入被组织为每行一条记录且格式可变的行,那么最好一次读取整行,也许使用 fgets()
,并且然后将结果解析到它的字段中。有许多可供选择的解析方法,其中包括 sscanf()
、strtok()
和 strtol()
.
如果您必须通过fscanf()
直接扫描每个字段来完成这项工作,那么这是可能的,但很麻烦。您提出的特定方法并不可靠:它不会识别带有尾随空格的行上的换行符,也不会识别因尾随数字为奇数而格式错误的行。
例如,您可以改用这样的东西:
/*
* 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);
}
这将允许您使用 scanf
一次扫描一个数字,识别行尾和格式错误的输入。